From 2cd6d416b2a5954892cced545abdce2afb2c31c6 Mon Sep 17 00:00:00 2001 From: eliempje Date: Sat, 12 Apr 2014 13:38:39 +0200 Subject: [PATCH 001/168] Bugfix: EPS thumbnail failed EPS thumbnail failed to resize correctly due to incorrect resolution argument (should be a function of the image size and bounding box). This is fixed in this commit. --- PIL/EpsImagePlugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 4d19c1f20..0857dae24 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -78,7 +78,8 @@ def Ghostscript(tile, size, fp, scale=1): orig_bbox = bbox size = (size[0] * scale, size[1] * scale) bbox = [bbox[0], bbox[1], bbox[2] * scale, bbox[3] * scale] - #print("Ghostscript", scale, size, orig_size, bbox, orig_bbox) + res = ( int((72 * size[0]) / (bbox[2]-bbox[0])), int((72 * size[1]) / (bbox[3]-bbox[1])) ) + #print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res) import tempfile, os, subprocess @@ -100,7 +101,7 @@ def Ghostscript(tile, size, fp, scale=1): command = ["gs", "-q", # quiet mode "-g%dx%d" % size, # set output geometry (pixels) - "-r%d" % (72*scale), # set input DPI (dots per inch) + "-r%dx%d" % res, # set input DPI (dots per inch) "-dNOPAUSE -dSAFER", # don't pause between pages, safe mode "-sDEVICE=ppmraw", # ppm driver "-sOutputFile=%s" % outfile, # output file From 5cd454bde27eab02dbcfc0adf0657a018cf5db47 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 14 Apr 2014 12:30:32 +0300 Subject: [PATCH 002/168] Fix docstring printing from __main__, and pyflakes and some pep8 --- PIL/ImageCms.py | 154 +++++++++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 67 deletions(-) diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index c875712c1..5decaf704 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -1,19 +1,19 @@ -# -# The Python Imaging Library. -# $Id$ -# -# optional color managment support, based on Kevin Cazabon's PyCMS -# library. -# -# History: -# 2009-03-08 fl Added to PIL. -# -# Copyright (C) 2002-2003 Kevin Cazabon -# Copyright (c) 2009 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. See -# below for the original description. -# +""" +The Python Imaging Library. +$Id$ + +Optional color managment support, based on Kevin Cazabon's PyCMS +library. + +History: +2009-03-08 fl Added to PIL. + +Copyright (C) 2002-2003 Kevin Cazabon +Copyright (c) 2009 by Fredrik Lundh + +See the README file for information on usage and redistribution. See +below for the original description. +""" from __future__ import print_function @@ -88,7 +88,7 @@ try: from PIL import _imagingcms except ImportError as ex: # Allow error import for doc purposes, but error out when accessing - # anything in core. + # anything in core. from _util import import_err _imagingcms = import_err(ex) from PIL._util import isStringType @@ -113,22 +113,22 @@ DIRECTION_PROOF = 2 FLAGS = { "MATRIXINPUT": 1, "MATRIXOUTPUT": 2, - "MATRIXONLY": (1|2), - "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot - "NOPRELINEARIZATION": 16, # Don't create prelinearization tables on precalculated transforms (internal use) - "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) - "NOTCACHE": 64, # Inhibit 1-pixel cache + "MATRIXONLY": (1 | 2), + "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot + "NOPRELINEARIZATION": 16, # Don't create prelinearization tables on precalculated transforms (internal use) + "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) + "NOTCACHE": 64, # Inhibit 1-pixel cache "NOTPRECALC": 256, - "NULLTRANSFORM": 512, # Don't transform anyway - "HIGHRESPRECALC": 1024, # Use more memory to give better accurancy - "LOWRESPRECALC": 2048, # Use less memory to minimize resouces + "NULLTRANSFORM": 512, # Don't transform anyway + "HIGHRESPRECALC": 1024, # Use more memory to give better accurancy + "LOWRESPRECALC": 2048, # Use less memory to minimize resouces "WHITEBLACKCOMPENSATION": 8192, "BLACKPOINTCOMPENSATION": 8192, - "GAMUTCHECK": 4096, # Out of Gamut alarm - "SOFTPROOFING": 16384, # Do softproofing - "PRESERVEBLACK": 32768, # Black preservation - "NODEFAULTRESOURCEDEF": 16777216, # CRD special - "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints + "GAMUTCHECK": 4096, # Out of Gamut alarm + "SOFTPROOFING": 16384, # Do softproofing + "PRESERVEBLACK": 32768, # Black preservation + "NODEFAULTRESOURCEDEF": 16777216, # CRD special + "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints } _MAX_FLAG = 0 @@ -136,6 +136,7 @@ for flag in FLAGS.values(): if isinstance(flag, int): _MAX_FLAG = _MAX_FLAG | flag + # --------------------------------------------------------------------. # Experimental PIL-level API # --------------------------------------------------------------------. @@ -153,18 +154,19 @@ class ImageCmsProfile: elif hasattr(profile, "read"): self._set(core.profile_frombytes(profile.read())) else: - self._set(profile) # assume it's already a profile + self._set(profile) # assume it's already a profile def _set(self, profile, filename=None): self.profile = profile self.filename = filename if profile: - self.product_name = None #profile.product_name - self.product_info = None #profile.product_info + self.product_name = None # profile.product_name + self.product_info = None # profile.product_info else: self.product_name = None self.product_info = None + class ImageCmsTransform(Image.ImagePointHandler): """Transform. This can be used with the procedural API, or with the standard Image.point() method. @@ -179,14 +181,14 @@ class ImageCmsTransform(Image.ImagePointHandler): input_mode, output_mode, intent, flags - ) + ) else: self.transform = core.buildProofTransform( input.profile, output.profile, proof.profile, input_mode, output_mode, intent, proof_intent, flags - ) + ) # Note: inputMode and outputMode are for pyCMS compatibility only self.input_mode = self.inputMode = input_mode self.output_mode = self.outputMode = output_mode @@ -198,21 +200,22 @@ class ImageCmsTransform(Image.ImagePointHandler): im.load() if imOut is None: imOut = Image.new(self.output_mode, im.size, None) - result = self.transform.apply(im.im.id, imOut.im.id) + self.transform.apply(im.im.id, imOut.im.id) return imOut def apply_in_place(self, im): im.load() if im.mode != self.output_mode: - raise ValueError("mode mismatch") # wrong output mode - result = self.transform.apply(im.im.id, im.im.id) + raise ValueError("mode mismatch") # wrong output mode + self.transform.apply(im.im.id, im.im.id) return im + def get_display_profile(handle=None): """ (experimental) Fetches the profile for the current display device. :returns: None if the profile is not known. """ - + import sys if sys.platform == "win32": from PIL import ImageWin @@ -229,6 +232,7 @@ def get_display_profile(handle=None): profile = get() return ImageCmsProfile(profile) + # --------------------------------------------------------------------. # pyCMS compatible layer # --------------------------------------------------------------------. @@ -237,6 +241,7 @@ class PyCMSError(Exception): """ (pyCMS) Exception class. This is used for all errors in the pyCMS API. """ pass + def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, outputMode=None, inPlace=0, flags=0): """ (pyCMS) Applies an ICC transformation to a given image, mapping from @@ -288,7 +293,7 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER if outputMode is None: outputMode = im.mode - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): @@ -300,8 +305,9 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER if not isinstance(outputProfile, ImageCmsProfile): outputProfile = ImageCmsProfile(outputProfile) transform = ImageCmsTransform( - inputProfile, outputProfile, im.mode, outputMode, renderingIntent, flags=flags - ) + inputProfile, outputProfile, im.mode, outputMode, + renderingIntent, flags=flags + ) if inPlace: transform.apply_in_place(im) imOut = None @@ -334,6 +340,7 @@ def getOpenProfile(profileFilename): except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) + def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, flags=0): """ (pyCMS) Builds an ICC transform mapping from the inputProfile to the @@ -389,7 +396,7 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent :exception PyCMSError: """ - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): @@ -404,6 +411,7 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) + def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"]): """ (pyCMS) Builds an ICC transform mapping from the inputProfile to the @@ -476,8 +484,8 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo :returns: A CmsTransform class object. :exception PyCMSError: """ - - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): + + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): @@ -497,6 +505,7 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo buildTransformFromOpenProfiles = buildTransform buildProofTransformFromOpenProfiles = buildProofTransform + def applyTransform(im, transform, inPlace=0): """ (pyCMS) Applies a transform to a given image. @@ -546,6 +555,7 @@ def applyTransform(im, transform, inPlace=0): return imOut + def createProfile(colorSpace, colorTemp=-1): """ (pyCMS) Creates a profile. @@ -586,6 +596,7 @@ def createProfile(colorSpace, colorTemp=-1): except (TypeError, ValueError) as v: raise PyCMSError(v) + def getProfileName(profile): """ @@ -612,14 +623,14 @@ def getProfileName(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) # do it in python, not c. - # // name was "%s - %s" (model, manufacturer) || Description , - # // but if the Model and Manufacturer were the same or the model + # // name was "%s - %s" (model, manufacturer) || Description , + # // but if the Model and Manufacturer were the same or the model # // was long, Just the model, in 1.x model = profile.profile.product_model manufacturer = profile.profile.product_manufacturer if not (model or manufacturer): - return profile.profile.product_description+"\n" + return profile.profile.product_description + "\n" if not manufacturer or len(model) > 30: return model + "\n" return "%s - %s\n" % (model, manufacturer) @@ -627,6 +638,7 @@ def getProfileName(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) + def getProfileInfo(profile): """ (pyCMS) Gets the internal product information for the given profile. @@ -647,7 +659,7 @@ def getProfileInfo(profile): tag. :exception PyCMSError: """ - + try: if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) @@ -660,7 +672,7 @@ def getProfileInfo(profile): for elt in (description, cpright): if elt: arr.append(elt) - return "\r\n\r\n".join(arr)+"\r\n\r\n" + return "\r\n\r\n".join(arr) + "\r\n\r\n" except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -677,7 +689,7 @@ def getProfileCopyright(profile): is raised Use this function to obtain the information stored in the profile's - copyright tag. + copyright tag. :param profile: EITHER a valid CmsProfile object, OR a string of the filename of an ICC profile. @@ -693,6 +705,7 @@ def getProfileCopyright(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) + def getProfileManufacturer(profile): """ (pyCMS) Gets the manufacturer for the given profile. @@ -704,7 +717,7 @@ def getProfileManufacturer(profile): is raised Use this function to obtain the information stored in the profile's - manufacturer tag. + manufacturer tag. :param profile: EITHER a valid CmsProfile object, OR a string of the filename of an ICC profile. @@ -720,19 +733,20 @@ def getProfileManufacturer(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) + def getProfileModel(profile): """ (pyCMS) Gets the model for the given profile. - + If profile isn't a valid CmsProfile object or filename to a profile, a PyCMSError is raised. - + If an error occurs while trying to obtain the model tag, a PyCMSError is raised - + Use this function to obtain the information stored in the profile's - model tag. - + model tag. + :param profile: EITHER a valid CmsProfile object, OR a string of the filename of an ICC profile. :returns: A string containing the internal profile information stored in an ICC @@ -748,6 +762,7 @@ def getProfileModel(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) + def getProfileDescription(profile): """ (pyCMS) Gets the description for the given profile. @@ -759,7 +774,7 @@ def getProfileDescription(profile): is raised Use this function to obtain the information stored in the profile's - description tag. + description tag. :param profile: EITHER a valid CmsProfile object, OR a string of the filename of an ICC profile. @@ -813,6 +828,7 @@ def getDefaultIntent(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) + def isIntentSupported(profile, intent, direction): """ (pyCMS) Checks if a given intent is supported. @@ -862,15 +878,17 @@ def isIntentSupported(profile, intent, direction): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) + def versions(): """ (pyCMS) Fetches versions. """ - + import sys return ( - VERSION, core.littlecms_version, sys.version.split()[0], Image.VERSION - ) + VERSION, core.littlecms_version, + sys.version.split()[0], Image.VERSION + ) # -------------------------------------------------------------------- @@ -880,14 +898,16 @@ if __name__ == "__main__": from PIL import ImageCms print(__doc__) - for f in dir(pyCMS): - print("="*80) - print("%s" %f) - + for f in dir(ImageCms): + doc = None try: - exec ("doc = ImageCms.%s.__doc__" %(f)) + exec("doc = %s.__doc__" % (f)) if "pyCMS" in doc: # so we don't get the __doc__ string for imported modules + print("=" * 80) + print("%s" % f) print(doc) - except AttributeError: + except (AttributeError, TypeError): pass + +# End of file From a97e5039d83bdb862b577196dc0736173f15f09a Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 14 Apr 2014 12:51:12 +0300 Subject: [PATCH 003/168] Remove unused _binary import (plus flake8) --- PIL/Jpeg2KImagePlugin.py | 41 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index f57f4a784..ee8fc46aa 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -15,20 +15,21 @@ __version__ = "0.1" -from PIL import Image, ImageFile, _binary +from PIL import Image, ImageFile import struct import os import io + def _parse_codestream(fp): """Parse the JPEG 2000 codestream to extract the size and component count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" - + hdr = fp.read(2) lsiz = struct.unpack('>H', hdr)[0] siz = hdr + fp.read(lsiz - 2) lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \ - xtosiz, ytosiz, csiz \ + xtosiz, ytosiz, csiz \ = struct.unpack('>HHIIIIIIIIH', siz[:38]) ssiz = [None]*csiz xrsiz = [None]*csiz @@ -48,13 +49,14 @@ def _parse_codestream(fp): mode == 'RGBA' else: mode = None - + return (size, mode) + def _parse_jp2_header(fp): """Parse the JP2 header box to extract size, component count and color space information, returning a PIL (size, mode) tuple.""" - + # Find the JP2 header box header = None while True: @@ -76,7 +78,7 @@ def _parse_jp2_header(fp): size = None mode = None - + hio = io.BytesIO(header) while True: lbox, tbox = struct.unpack('>I4s', hio.read(8)) @@ -90,7 +92,7 @@ def _parse_jp2_header(fp): if tbox == b'ihdr': height, width, nc, bpc, c, unkc, ipr \ - = struct.unpack('>IIHBBBB', content) + = struct.unpack('>IIHBBBB', content) size = (width, height) if unkc: if nc == 1: @@ -112,13 +114,13 @@ def _parse_jp2_header(fp): elif nc == 4: mode = 'RGBA' break - elif cs == 17: # grayscale + elif cs == 17: # grayscale if nc == 1: mode = 'L' elif nc == 2: mode = 'LA' break - elif cs == 18: # sYCC + elif cs == 18: # sYCC if nc == 3: mode = 'RGB' elif nc == 4: @@ -127,6 +129,7 @@ def _parse_jp2_header(fp): return (size, mode) + ## # Image plugin for JPEG2000 images. @@ -141,16 +144,16 @@ class Jpeg2KImageFile(ImageFile.ImageFile): self.size, self.mode = _parse_codestream(self.fp) else: sig = sig + self.fp.read(8) - + if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a': self.codec = "jp2" self.size, self.mode = _parse_jp2_header(self.fp) else: raise SyntaxError('not a JPEG 2000 file') - + if self.size is None or self.mode is None: raise SyntaxError('unable to determine size/mode') - + self.reduce = 0 self.layers = 0 @@ -177,13 +180,15 @@ class Jpeg2KImageFile(ImageFile.ImageFile): t = self.tile[0] t3 = (t[3][0], self.reduce, self.layers, t[3][3]) self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] - + ImageFile.ImageFile.load(self) - + + def _accept(prefix): return (prefix[:4] == b'\xff\x4f\xff\x51' or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a') + # ------------------------------------------------------------ # Save support @@ -214,7 +219,7 @@ def _save(im, fp, filename): fd = fp.fileno() except: fd = -1 - + im.encoderconfig = ( offset, tile_offset, @@ -228,10 +233,10 @@ def _save(im, fp, filename): progression, cinema_mode, fd - ) - + ) + ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)]) - + # ------------------------------------------------------------ # Registry stuff From adfbe8323aa9daf453872fd60561a865b95beb24 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 22 Apr 2014 08:54:16 +0300 Subject: [PATCH 004/168] Tests and partial implementation of pickling (#629) --- PIL/Image.py | 47 ++++++++++++++++++++------------- Tests/test_pickle.py | 63 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 Tests/test_pickle.py diff --git a/PIL/Image.py b/PIL/Image.py index 333397701..c74971b1d 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -101,7 +101,7 @@ import collections import numbers # works everywhere, win for pypy, not cpython -USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') +USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') try: import cffi HAS_CFFI=True @@ -233,7 +233,7 @@ _MODE_CONV = { "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 - # I;16 == I;16L, and I;32 == I;32L + # I;16 == I;16L, and I;32 == I;32L "I;16": ('u2', None), "I;16L": (' 8bit images. + # a gamma function point transform on > 8bit images. scale, offset = _getscaleoffset(lut) return self._new(self.im.point_transform(scale, offset)) # for other modes, convert the function to a table @@ -1420,8 +1429,8 @@ class Image: self._copy() self.pyaccess = None self.load() - - if self.pyaccess: + + if self.pyaccess: return self.pyaccess.putpixel(xy,value) return self.im.putpixel(xy, value) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py new file mode 100644 index 000000000..a40d26088 --- /dev/null +++ b/Tests/test_pickle.py @@ -0,0 +1,63 @@ +from tester import * + +from PIL import Image + + +def test_frombytes_tobytes(): + # Arrange + im = Image.open('Images/lena.jpg') + + # Act + data = im.tobytes() + new_im = Image.frombytes(im.mode, im.size, data) + + # Assert + assert_image_equal(im, new_im) + + +def helper_test_pickle_file(pickle, protocol=0): + im = Image.open('Images/lena.jpg') + filename = tempfile('temp.pkl') + + # Act + with open(filename, 'wb') as f: + pickle.dump(im, f, protocol) + with open(filename, 'rb') as f: + loaded_im = pickle.load(f) + + # Assert + assert_image_equal(im, loaded_im) + + +def helper_test_pickle_string(pickle, protocol=0): + im = Image.open('Images/lena.jpg') + + # Act + dumped_string = pickle.dumps(im, protocol) + loaded_im = pickle.loads(dumped_string) + + # Assert + assert_image_equal(im, loaded_im) + + +def test_pickle_image(): + # Arrange + import pickle + + # Act / Assert + for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): + helper_test_pickle_string(pickle, protocol) + helper_test_pickle_file(pickle, protocol) + + +def test_cpickle_image(): + # Arrange + import cPickle + + # Act / Assert + for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1): + helper_test_pickle_string(cPickle, protocol) + helper_test_pickle_file(cPickle, protocol) + + +# End of file From a3edb45f08560f5661034b62a53678270b7465bf Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 22 Apr 2014 09:23:34 +0300 Subject: [PATCH 005/168] pep8 --- PIL/Image.py | 166 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 63 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index c74971b1d..d7db1fe1e 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -30,6 +30,7 @@ from PIL import VERSION, PILLOW_VERSION, _plugins import warnings + class _imaging_not_installed: # module placeholder def __getattr__(self, id): @@ -52,8 +53,8 @@ try: # directly; import Image and use the Image.core variable instead. from PIL import _imaging as core if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None): - raise ImportError("The _imaging extension was built for another " - " version of Pillow or PIL") + raise ImportError("The _imaging extension was built for another " + " version of Pillow or PIL") except ImportError as v: core = _imaging_not_installed() @@ -94,7 +95,8 @@ from PIL import ImageMode from PIL._binary import i8, o8 from PIL._util import isPath, isStringType, deferred_error -import os, sys +import os +import sys # type stuff import collections @@ -104,9 +106,10 @@ import numbers USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') try: import cffi - HAS_CFFI=True + HAS_CFFI = True except: - HAS_CFFI=False + HAS_CFFI = False + def isImageType(t): """ @@ -148,16 +151,16 @@ MESH = 4 # resampling filters NONE = 0 NEAREST = 0 -ANTIALIAS = 1 # 3-lobed lanczos +ANTIALIAS = 1 # 3-lobed lanczos LINEAR = BILINEAR = 2 CUBIC = BICUBIC = 3 # dithers NONE = 0 NEAREST = 0 -ORDERED = 1 # Not yet implemented -RASTERIZE = 2 # Not yet implemented -FLOYDSTEINBERG = 3 # default +ORDERED = 1 # Not yet implemented +RASTERIZE = 2 # Not yet implemented +FLOYDSTEINBERG = 3 # default # palettes/quantizers WEB = 0 @@ -222,7 +225,7 @@ else: _MODE_CONV = { # official modes - "1": ('|b1', None), # broken + "1": ('|b1', None), # broken "L": ('|u1', None), "I": (_ENDIAN + 'i4', None), "F": (_ENDIAN + 'f4', None), @@ -232,8 +235,8 @@ _MODE_CONV = { "RGBA": ('|u1', 4), "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), - "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 - # I;16 == I;16L, and I;32 == I;32L + "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 + # I;16 == I;16L, and I;32 == I;32L "I;16": ('u2', None), "I;16L": (' size[0]: y = int(max(y * size[0] / x, 1)); x = int(size[0]) - if y > size[1]: x = int(max(x * size[1] / y, 1)); y = int(size[1]) + if x > size[0]: + y = int(max(y * size[0] / x, 1)) + x = int(size[0]) + if y > size[1]: + x = int(max(x * size[1] / y, 1)) + y = int(size[1]) size = x, y if size == self.size: @@ -1725,7 +1751,7 @@ class Image: except ValueError: if resample != ANTIALIAS: raise - im = self.resize(size, NEAREST) # fallback + im = self.resize(size, NEAREST) # fallback self.im = im.im self.mode = im.mode @@ -1761,7 +1787,8 @@ class Image: """ if self.mode == 'RGBA': - return self.convert('RGBa').transform(size, method, data, resample, fill).convert('RGBA') + return self.convert('RGBa').transform( + size, method, data, resample, fill).convert('RGBA') if isinstance(method, ImageTransformHandler): return method.transform(size, self, resample=resample, fill=fill) @@ -1808,8 +1835,13 @@ class Image: elif method == QUAD: # quadrilateral warp. data specifies the four corners # given as NW, SW, SE, and NE. - nw = data[0:2]; sw = data[2:4]; se = data[4:6]; ne = data[6:8] - x0, y0 = nw; As = 1.0 / w; At = 1.0 / h + nw = data[0:2] + sw = data[2:4] + se = data[4:6] + ne = data[6:8] + x0, y0 = nw + As = 1.0 / w + At = 1.0 / h data = (x0, (ne[0]-x0)*As, (sw[0]-x0)*At, (se[0]-sw[0]-ne[0]+x0)*As*At, y0, (ne[1]-y0)*As, (sw[1]-y0)*At, @@ -1843,6 +1875,7 @@ class Image: im = self.im.transpose(method) return self._new(im) + # -------------------------------------------------------------------- # Lazy operations @@ -1878,6 +1911,7 @@ class _ImageCrop(Image): # FIXME: future versions should optimize crop/paste # sequences! + # -------------------------------------------------------------------- # Abstract handlers. @@ -1885,10 +1919,12 @@ class ImagePointHandler: # used as a mixin by point transforms (for use with im.point) pass + class ImageTransformHandler: # used as a mixin by geometry transforms (for use with im.transform) pass + # -------------------------------------------------------------------- # Factories @@ -1964,6 +2000,7 @@ def frombytes(mode, size, data, decoder_name="raw", *args): im.frombytes(data, decoder_name, args) return im + def fromstring(*args, **kw): """Deprecated alias to frombytes. @@ -2026,9 +2063,9 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): " frombuffer(mode, size, data, 'raw', mode, 0, 1)", RuntimeWarning, stacklevel=2 ) - args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6 + args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6 if args[0] in _MAPMODES: - im = new(mode, (1,1)) + im = new(mode, (1, 1)) im = im._new( core.map_buffer(data, size, decoder_name, None, 0, args) ) @@ -2168,6 +2205,7 @@ def open(fp, mode="r"): raise IOError("cannot identify image file %r" % (filename if filename else fp)) + # # Image processing. @@ -2266,6 +2304,7 @@ def merge(mode, bands): im.putband(bands[i].im, i) return bands[0]._new(im) + # -------------------------------------------------------------------- # Plugin registry @@ -2324,6 +2363,7 @@ def _show(image, **options): # override me, as necessary _showxv(image, **options) + def _showxv(image, title=None, **options): from PIL import ImageShow ImageShow.show(image, title, **options) From e4185694a2d900fed994b0fede818d98683c3021 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 22 Apr 2014 09:30:15 +0300 Subject: [PATCH 006/168] P3 will skip explicit cPickle tests --- Tests/test_pickle.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index a40d26088..b52932757 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -52,12 +52,14 @@ def test_pickle_image(): def test_cpickle_image(): # Arrange - import cPickle + try: + import cPickle + except ImportError: + skip() # Act / Assert for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1): helper_test_pickle_string(cPickle, protocol) helper_test_pickle_file(cPickle, protocol) - # End of file From 6802c12f89a7cea013febe4fd4d53327d1c94ed9 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 25 Apr 2014 09:01:16 +0300 Subject: [PATCH 007/168] Initialise object when unpickling --- PIL/Image.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index d7db1fe1e..40caedbe5 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -585,9 +585,16 @@ class Image: return [self.mode, self.size, self.tobytes()] def __setstate__(self, state): + self.category = NORMAL + self.info = {} + self.palette = None + self.pyaccess = None + self.readonly = 0 + self.tile = [] mode, size, data = state self.mode = mode self.size = size + self.im = core.new(mode, size) self.frombytes(data) def tobytes(self, encoder_name="raw", *args): From 6c938b784b55fd4858384172e8a8d4bb98191e6d Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 26 Apr 2014 17:18:29 +0300 Subject: [PATCH 008/168] Remove duplication by calling __init__() (Suggested by @ulope: https://github.com/hugovk/Pillow/commit/6802c12f89a7cea013febe4fd4d53327d1c94ed9#commitcomment-6125853) --- PIL/Image.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 40caedbe5..889e92303 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -585,11 +585,7 @@ class Image: return [self.mode, self.size, self.tobytes()] def __setstate__(self, state): - self.category = NORMAL - self.info = {} - self.palette = None - self.pyaccess = None - self.readonly = 0 + Image.__init__(self) self.tile = [] mode, size, data = state self.mode = mode From 2a6f2c5442d1095ed755d4fa699169f68bfceb1e Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 26 Apr 2014 19:43:53 +0300 Subject: [PATCH 009/168] Add __eq__ and __ne__ to Image to be able to test image equality when pickling. Pickle more data. --- PIL/Image.py | 37 ++++++++++++++++++++++++++++++------- Tests/test_pickle.py | 33 +++++++++++++++++++-------------- Tests/tester.py | 3 ++- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 889e92303..8d97e1221 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -563,6 +563,21 @@ class Image: self.save(file, format) return file + def __eq__(self, other): + a = (self.mode == other.mode) + b = (self.size == other.size) + c = (self.getpalette() == other.getpalette()) + d = (self.info == other.info) + e = (self.category == other.category) + f = (self.readonly == other.readonly) + g = (self.pyaccess == other.pyaccess) + h = (self.tobytes() == other.tobytes()) + return a and b and c and d and e and f and g and h + + def __ne__(self, other): + eq = (self == other) + return not eq + def __repr__(self): return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( self.__class__.__module__, self.__class__.__name__, @@ -582,15 +597,23 @@ class Image: raise AttributeError(name) def __getstate__(self): - return [self.mode, self.size, self.tobytes()] + return [ + self.info, + self.mode, + self.size, + self.getpalette(), + self.tobytes()] def __setstate__(self, state): Image.__init__(self) self.tile = [] - mode, size, data = state + info, mode, size, palette, data = state + self.info = info self.mode = mode self.size = size self.im = core.new(mode, size) + if mode in ("L", "P"): + self.putpalette(palette) self.frombytes(data) def tobytes(self, encoder_name="raw", *args): @@ -870,7 +893,7 @@ class Image: new_im = self._new(im) if delete_trns: - #crash fail if we leave a bytes transparency in an rgb/l mode. + # crash fail if we leave a bytes transparency in an rgb/l mode. del(new_im.info['transparency']) if trns is not None: if new_im.mode == 'P': @@ -2188,8 +2211,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): - #import traceback - #traceback.print_exc() + # import traceback + # traceback.print_exc() pass if init(): @@ -2201,8 +2224,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): - #import traceback - #traceback.print_exc() + # import traceback + # traceback.print_exc() pass raise IOError("cannot identify image file %r" diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index b52932757..7da385624 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -3,18 +3,6 @@ from tester import * from PIL import Image -def test_frombytes_tobytes(): - # Arrange - im = Image.open('Images/lena.jpg') - - # Act - data = im.tobytes() - new_im = Image.frombytes(im.mode, im.size, data) - - # Assert - assert_image_equal(im, new_im) - - def helper_test_pickle_file(pickle, protocol=0): im = Image.open('Images/lena.jpg') filename = tempfile('temp.pkl') @@ -29,8 +17,8 @@ def helper_test_pickle_file(pickle, protocol=0): assert_image_equal(im, loaded_im) -def helper_test_pickle_string(pickle, protocol=0): - im = Image.open('Images/lena.jpg') +def helper_test_pickle_string(pickle, protocol=0, file='Images/lena.jpg'): + im = Image.open(file) # Act dumped_string = pickle.dumps(im, protocol) @@ -62,4 +50,21 @@ def test_cpickle_image(): helper_test_pickle_string(cPickle, protocol) helper_test_pickle_file(cPickle, protocol) + +def test_pickle_p_mode(): + # Arrange + import pickle + + # Act / Assert + for file in [ + "Tests/images/test-card.png", + "Tests/images/zero_bb.png", + "Tests/images/zero_bb_scale2.png", + "Tests/images/non_zero_bb.png", + "Tests/images/non_zero_bb_scale2.png", + "Tests/images/p_trns_single.png", + "Tests/images/pil123p.png" + ]: + helper_test_pickle_string(pickle, file=file) + # End of file diff --git a/Tests/tester.py b/Tests/tester.py index 32da48e98..a58872e2c 100644 --- a/Tests/tester.py +++ b/Tests/tester.py @@ -242,7 +242,8 @@ def assert_image_equal(a, b, msg=None): failure(msg or "got size %r, expected %r" % (a.size, b.size)) elif a.tobytes() != b.tobytes(): failure(msg or "got different content") - # generate better diff? + elif a != b: + failure(msg or "images different") else: success() From 8794c463181b94dc39cab8bd368efe809cc9adde Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 26 Apr 2014 20:05:02 +0300 Subject: [PATCH 010/168] For pickling, test with Image's __eq__. Everything else can use the old assert_image_equal. --- Tests/test_pickle.py | 4 ++-- Tests/tester.py | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index 7da385624..ac8e0545d 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -14,7 +14,7 @@ def helper_test_pickle_file(pickle, protocol=0): loaded_im = pickle.load(f) # Assert - assert_image_equal(im, loaded_im) + assert_image_completely_equal(im, loaded_im) def helper_test_pickle_string(pickle, protocol=0, file='Images/lena.jpg'): @@ -25,7 +25,7 @@ def helper_test_pickle_string(pickle, protocol=0, file='Images/lena.jpg'): loaded_im = pickle.loads(dumped_string) # Assert - assert_image_equal(im, loaded_im) + assert_image_completely_equal(im, loaded_im) def test_pickle_image(): diff --git a/Tests/tester.py b/Tests/tester.py index a58872e2c..c1e8404d7 100644 --- a/Tests/tester.py +++ b/Tests/tester.py @@ -242,7 +242,12 @@ def assert_image_equal(a, b, msg=None): failure(msg or "got size %r, expected %r" % (a.size, b.size)) elif a.tobytes() != b.tobytes(): failure(msg or "got different content") - elif a != b: + else: + success() + + +def assert_image_completely_equal(a, b, msg=None): + if a != b: failure(msg or "images different") else: success() From 7d1cdc54c433855cf560b6305db5b38baa278a08 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 26 Apr 2014 20:20:42 +0300 Subject: [PATCH 011/168] Don't skip all the tests on Python 3 when there's no cPickle, just the irrelevant test --- Tests/test_pickle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index ac8e0545d..34240f19a 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -41,9 +41,9 @@ def test_pickle_image(): def test_cpickle_image(): # Arrange try: - import cPickle + import ABCcPickle except ImportError: - skip() + return # Act / Assert for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1): From a7d21dec154e146ba20d16f3b9efc32363a5708d Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 26 Apr 2014 20:41:01 +0300 Subject: [PATCH 012/168] Remove temp test code --- Tests/test_pickle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index 34240f19a..d36f476d0 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -41,7 +41,7 @@ def test_pickle_image(): def test_cpickle_image(): # Arrange try: - import ABCcPickle + import cPickle except ImportError: return From f1cc19495570d2f557349c023d740f29b7943033 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 26 Apr 2014 21:23:45 +0300 Subject: [PATCH 013/168] Don't compare pyaccess in __eq__ --- PIL/Image.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 8d97e1221..9424f4bd2 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -570,9 +570,8 @@ class Image: d = (self.info == other.info) e = (self.category == other.category) f = (self.readonly == other.readonly) - g = (self.pyaccess == other.pyaccess) - h = (self.tobytes() == other.tobytes()) - return a and b and c and d and e and f and g and h + g = (self.tobytes() == other.tobytes()) + return a and b and c and d and e and f and g def __ne__(self, other): eq = (self == other) From d2fbc52d6c88fd01fd8283158ddcb0b45a36c4a4 Mon Sep 17 00:00:00 2001 From: Nicolas F Date: Wed, 30 Apr 2014 11:30:44 +0200 Subject: [PATCH 014/168] Change unsigned INT16 to UINT16 Addresses issue #642 --- libImaging/Gif.h | 2 +- libImaging/Lzw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libImaging/Gif.h b/libImaging/Gif.h index 85d428b0d..2cb95efd2 100644 --- a/libImaging/Gif.h +++ b/libImaging/Gif.h @@ -59,7 +59,7 @@ typedef struct { unsigned char buffer[GIFTABLE]; /* Symbol table */ - unsigned INT16 link[GIFTABLE]; + UINT16 link[GIFTABLE]; unsigned char data[GIFTABLE]; int next; diff --git a/libImaging/Lzw.h b/libImaging/Lzw.h index 7d087ac47..8fc30bd7f 100644 --- a/libImaging/Lzw.h +++ b/libImaging/Lzw.h @@ -45,7 +45,7 @@ typedef struct { unsigned char buffer[LZWTABLE]; /* Symbol table */ - unsigned INT16 link[LZWTABLE]; + UINT16 link[LZWTABLE]; unsigned char data[LZWTABLE]; int next; From e835dd70a1183aaab7adaa43a7f28221038d28c9 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 5 May 2014 22:09:57 +0300 Subject: [PATCH 015/168] Discard first byte if not 0xFF (for issue #630) --- PIL/JpegImagePlugin.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index da52006ca..757d02519 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -290,24 +290,29 @@ class JpegImageFile(ImageFile.ImageFile): while True: - s = s + self.fp.read(1) - - i = i16(s) + i = i8(s) + if i == 0xFF: + s = s + self.fp.read(1) + i = i16(s) + else: + # Skip non-0xFF junk + s = "\xff" + continue if i in MARKER: name, description, handler = MARKER[i] # print hex(i), name, description if handler is not None: handler(self, i) - if i == 0xFFDA: # start of scan + if i == 0xFFDA: # start of scan rawmode = self.mode if self.mode == "CMYK": - rawmode = "CMYK;I" # assume adobe conventions - self.tile = [("jpeg", (0,0) + self.size, 0, (rawmode, ""))] + rawmode = "CMYK;I" # assume adobe conventions + self.tile = [("jpeg", (0, 0) + self.size, 0, (rawmode, ""))] # self.__offset = self.fp.tell() break s = self.fp.read(1) - elif i == 0 or i == 65535: + elif i == 0 or i == 0xFFFF: # padded marker or junk; move on s = "\xff" else: From f4071ade0a995c02fcbc40ff1f3982bbbda2b4ff Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 5 May 2014 22:41:09 +0300 Subject: [PATCH 016/168] pep8 --- PIL/JpegImagePlugin.py | 69 +++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 757d02519..6f32878e5 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -34,7 +34,8 @@ __version__ = "0.6" -import array, struct +import array +import struct from PIL import Image, ImageFile, _binary from PIL.JpegPresets import presets from PIL._util import isStringType @@ -44,6 +45,7 @@ o8 = _binary.o8 i16 = _binary.i16be i32 = _binary.i32be + # # Parser @@ -51,6 +53,7 @@ def Skip(self, marker): n = i16(self.fp.read(2))-2 ImageFile._safe_read(self.fp, n) + def APP(self, marker): # # Application marker. Store these in the APP dictionary. @@ -59,14 +62,14 @@ def APP(self, marker): n = i16(self.fp.read(2))-2 s = ImageFile._safe_read(self.fp, n) - app = "APP%d" % (marker&15) + app = "APP%d" % (marker & 15) - self.app[app] = s # compatibility + self.app[app] = s # compatibility self.applist.append((app, s)) if marker == 0xFFE0 and s[:4] == b"JFIF": # extract JFIF information - self.info["jfif"] = version = i16(s, 5) # version + self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: @@ -81,10 +84,10 @@ def APP(self, marker): self.info["jfif_density"] = jfif_density elif marker == 0xFFE1 and s[:5] == b"Exif\0": # extract Exif information (incomplete) - self.info["exif"] = s # FIXME: value will change + self.info["exif"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) - self.info["flashpix"] = s # FIXME: value will change + self.info["flashpix"] = s # FIXME: value will change elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": # Since an ICC profile can be larger than the maximum size of # a JPEG marker (64K), we need provisions to split it into @@ -108,16 +111,17 @@ def APP(self, marker): else: self.info["adobe_transform"] = adobe_transform + def COM(self, marker): # # Comment marker. Store these in the APP dictionary. - n = i16(self.fp.read(2))-2 s = ImageFile._safe_read(self.fp, n) - self.app["COM"] = s # compatibility + self.app["COM"] = s # compatibility self.applist.append(("COM", s)) + def SOF(self, marker): # # Start of frame marker. Defines the size and mode of the @@ -149,21 +153,22 @@ def SOF(self, marker): if self.icclist: # fixup icc profile - self.icclist.sort() # sort by sequence number + self.icclist.sort() # sort by sequence number if i8(self.icclist[0][13]) == len(self.icclist): profile = [] for p in self.icclist: profile.append(p[14:]) icc_profile = b"".join(profile) else: - icc_profile = None # wrong number of fragments + icc_profile = None # wrong number of fragments self.info["icc_profile"] = icc_profile self.icclist = None for i in range(6, len(s), 3): t = s[i:i+3] # 4-tuples: id, vsamp, hsamp, qtable - self.layer.append((t[0], i8(t[1])//16, i8(t[1])&15, i8(t[2]))) + self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2]))) + def DQT(self, marker): # @@ -181,10 +186,10 @@ def DQT(self, marker): raise SyntaxError("bad quantization table marker") v = i8(s[0]) if v//16 == 0: - self.quantization[v&15] = array.array("b", s[1:65]) + self.quantization[v & 15] = array.array("b", s[1:65]) s = s[65:] else: - return # FIXME: add code to read 16-bit tables! + return # FIXME: add code to read 16-bit tables! # raise SyntaxError, "bad quantization table element size" @@ -261,6 +266,7 @@ MARKER = { def _accept(prefix): return prefix[0:1] == b"\377" + ## # Image plugin for JPEG and JFIF images. @@ -284,7 +290,7 @@ class JpegImageFile(ImageFile.ImageFile): self.huffman_dc = {} self.huffman_ac = {} self.quantization = {} - self.app = {} # compatibility + self.app = {} # compatibility self.applist = [] self.icclist = [] @@ -348,7 +354,8 @@ class JpegImageFile(ImageFile.ImageFile): # ALTERNATIVE: handle JPEGs via the IJG command line utilities - import tempfile, os + import tempfile + import os f, path = tempfile.mkstemp() os.close(f) if os.path.exists(self.filename): @@ -359,8 +366,10 @@ class JpegImageFile(ImageFile.ImageFile): try: self.im = Image.core.open_ppm(path) finally: - try: os.unlink(path) - except: pass + try: + os.unlink(path) + except: + pass self.mode = self.im.mode self.size = self.im.size @@ -377,6 +386,7 @@ def _getexif(self): # version. from PIL import TiffImagePlugin import io + def fixup(value): if len(value) == 1: return value[0] @@ -427,7 +437,7 @@ RAWMODE = { "RGB": "RGB", "RGBA": "RGB", "RGBX": "RGB", - "CMYK": "CMYK;I", # assume adobe conventions + "CMYK": "CMYK;I", # assume adobe conventions "YCbCr": "YCbCr", } @@ -446,16 +456,19 @@ samplings = { (2, 2, 1, 1, 1, 1): 2, } + def convert_dict_qtables(qtables): qtables = [qtables[key] for key in range(len(qtables)) if key in qtables] for idx, table in enumerate(qtables): qtables[idx] = [table[i] for i in zigzag_index] return qtables + def get_sampling(im): sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] return samplings.get(sampling, -1) + def _save(im, fp, filename): try: @@ -568,12 +581,11 @@ def _save(im, fp, filename): info.get("exif", b"") ) - - # if we optimize, libjpeg needs a buffer big enough to hold the whole image in a shot. - # Guessing on the size, at im.size bytes. (raw pizel size is channels*size, this - # is a value that's been used in a django patch. + # if we optimize, libjpeg needs a buffer big enough to hold the whole image + # in a shot. Guessing on the size, at im.size bytes. (raw pizel size is + # channels*size, this is a value that's been used in a django patch. # https://github.com/jdriscoll/django-imagekit/issues/50 - bufsize=0 + bufsize = 0 if "optimize" in info or "progressive" in info or "progression" in info: if quality >= 95: bufsize = 2 * im.size[0] * im.size[1] @@ -582,17 +594,20 @@ def _save(im, fp, filename): # The exif info needs to be written as one block, + APP1, + one spare byte. # Ensure that our buffer is big enough - bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif",b"")) + 5 ) + bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5) + + ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize) - ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize) def _save_cjpeg(im, fp, filename): # ALTERNATIVE: handle JPEGs via the IJG command line utilities. import os file = im._dump() os.system("cjpeg %s >%s" % (file, filename)) - try: os.unlink(file) - except: pass + try: + os.unlink(file) + except: + pass # -------------------------------------------------------------------q- # Registry stuff From d3192dd9302a8cdb7f52ed0b1fe09fbfab80fb8a Mon Sep 17 00:00:00 2001 From: eliempje Date: Wed, 7 May 2014 13:36:35 +0200 Subject: [PATCH 017/168] Update EpsImagePlugin.py FIXED issue #302: https://github.com/python-imaging/Pillow/issues/302 EPS file can have binary preview. Header is now also read binary. Also fix for resizing EPS. Resolution is now 2 dimensional and dependend on bbox and size. --- PIL/EpsImagePlugin.py | 66 ++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 0857dae24..1df5b5d61 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -11,6 +11,7 @@ # 1996-08-23 fl Handle files from Macintosh (0.3) # 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) # 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5) +# 2014-05-07 e Handling of EPS with binary preview and fixed resolution resizing # # Copyright (c) 1997-2003 by Secret Labs AB. # Copyright (c) 1995-2003 by Fredrik Lundh @@ -71,14 +72,14 @@ def Ghostscript(tile, size, fp, scale=1): # Unpack decoder tile decoder, tile, offset, data = tile[0] length, bbox = data - + #Hack to support hi-res rendering scale = int(scale) or 1 orig_size = size orig_bbox = bbox size = (size[0] * scale, size[1] * scale) - bbox = [bbox[0], bbox[1], bbox[2] * scale, bbox[3] * scale] - res = ( int((72 * size[0]) / (bbox[2]-bbox[0])), int((72 * size[1]) / (bbox[3]-bbox[1])) ) + # resolution is dependend on bbox and size + res = ( float((72.0 * size[0]) / (bbox[2]-bbox[0])), float((72.0 * size[1]) / (bbox[3]-bbox[1])) ) #print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res) import tempfile, os, subprocess @@ -87,21 +88,30 @@ def Ghostscript(tile, size, fp, scale=1): os.close(out_fd) in_fd, infile = tempfile.mkstemp() os.close(in_fd) - + + # ignore length and offset! + # ghostscript can read it + # copy whole file to read in ghostscript with open(infile, 'wb') as f: - fp.seek(offset) - while length >0: - s = fp.read(100*1024) + # fetch length of fp + fp.seek(0, 2) + fsize = fp.tell() + # ensure start position + # go back + fp.seek(0) + lengthfile = fsize + while lengthfile > 0: + s = fp.read(min(lengthfile, 100*1024)) if not s: break - length = length - len(s) + lengthfile = lengthfile - len(s) f.write(s) # Build ghostscript command command = ["gs", "-q", # quiet mode "-g%dx%d" % size, # set output geometry (pixels) - "-r%dx%d" % res, # set input DPI (dots per inch) + "-r%fx%f" % res, # set input DPI (dots per inch) "-dNOPAUSE -dSAFER", # don't pause between pages, safe mode "-sDEVICE=ppmraw", # ppm driver "-sOutputFile=%s" % outfile, # output file @@ -109,7 +119,7 @@ def Ghostscript(tile, size, fp, scale=1): # adjust for image origin "-f", infile, # input file ] - + if gs_windows_binary is not None: if not gs_windows_binary: raise WindowsError('Unable to locate Ghostscript on paths') @@ -128,7 +138,7 @@ def Ghostscript(tile, size, fp, scale=1): os.unlink(outfile) os.unlink(infile) except: pass - + return im @@ -146,6 +156,8 @@ class PSFile: self.fp.seek(offset, whence) def read(self, count): return self.fp.read(count).decode('latin-1') + def readbinary(self, count): + return self.fp.read(count) def tell(self): pos = self.fp.tell() if self.char: @@ -183,26 +195,34 @@ class EpsImageFile(ImageFile.ImageFile): def _open(self): - # FIXME: should check the first 512 bytes to see if this - # really is necessary (platform-dependent, though...) - fp = PSFile(self.fp) - # HEAD - s = fp.read(512) + # FIX for: Some EPS file not handled correctly / issue #302 + # EPS can contain binary data + # or start directly with latin coding + # read header in both ways to handle both + # file types + # more info see http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf + + # for HEAD without binary preview + s = fp.read(4) + # for HEAD with binary preview + fp.seek(0) + sb = fp.readbinary(160) + if s[:4] == "%!PS": - offset = 0 fp.seek(0, 2) length = fp.tell() - elif i32(s) == 0xC6D3D0C5: - offset = i32(s[4:]) - length = i32(s[8:]) - fp.seek(offset) + offset = 0 + elif i32(sb[0:4]) == 0xC6D3D0C5: + offset = i32(sb[4:8]) + length = i32(sb[8:12]) else: raise SyntaxError("not an EPS file") + # go to offset - start of "%!PS" fp.seek(offset) - + box = None self.mode = "RGB" @@ -212,7 +232,7 @@ class EpsImageFile(ImageFile.ImageFile): # Load EPS header s = fp.readline() - + while s: if len(s) > 255: From 134295bfd40c33c16f06fb06d6bd91f8b25e963f Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 8 May 2014 11:43:04 +0300 Subject: [PATCH 018/168] Resize tests for https://github.com/python-imaging/Pillow/pull/619 (plus flake8) --- Tests/test_file_eps.py | 48 ++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 0041824b1..9b524e70d 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -1,25 +1,25 @@ from tester import * from PIL import Image, EpsImagePlugin -import sys import io if not EpsImagePlugin.has_ghostscript(): skip() -#Our two EPS test files (they are identical except for their bounding boxes) +# Our two EPS test files (they are identical except for their bounding boxes) file1 = "Tests/images/zero_bb.eps" file2 = "Tests/images/non_zero_bb.eps" -#Due to palletization, we'll need to convert these to RGB after load +# Due to palletization, we'll need to convert these to RGB after load file1_compare = "Tests/images/zero_bb.png" file1_compare_scale2 = "Tests/images/zero_bb_scale2.png" file2_compare = "Tests/images/non_zero_bb.png" file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png" + def test_sanity(): - #Regular scale + # Regular scale image1 = Image.open(file1) image1.load() assert_equal(image1.mode, "RGB") @@ -32,7 +32,7 @@ def test_sanity(): assert_equal(image2.size, (360, 252)) assert_equal(image2.format, "EPS") - #Double scale + # Double scale image1_scale2 = Image.open(file1) image1_scale2.load(scale=2) assert_equal(image1_scale2.mode, "RGB") @@ -45,54 +45,76 @@ def test_sanity(): assert_equal(image2_scale2.size, (720, 504)) assert_equal(image2_scale2.format, "EPS") + def test_file_object(): - #issue 479 + # issue 479 image1 = Image.open(file1) with open(tempfile('temp_file.eps'), 'wb') as fh: image1.save(fh, 'EPS') + def test_iobase_object(): - #issue 479 + # issue 479 image1 = Image.open(file1) with io.open(tempfile('temp_iobase.eps'), 'wb') as fh: image1.save(fh, 'EPS') + def test_render_scale1(): - #We need png support for these render test + # We need png support for these render test codecs = dir(Image.core) if "zip_encoder" not in codecs or "zip_decoder" not in codecs: skip("zip/deflate support not available") - #Zero bounding box + # Zero bounding box image1_scale1 = Image.open(file1) image1_scale1.load() image1_scale1_compare = Image.open(file1_compare).convert("RGB") image1_scale1_compare.load() assert_image_similar(image1_scale1, image1_scale1_compare, 5) - #Non-Zero bounding box + # Non-Zero bounding box image2_scale1 = Image.open(file2) image2_scale1.load() image2_scale1_compare = Image.open(file2_compare).convert("RGB") image2_scale1_compare.load() assert_image_similar(image2_scale1, image2_scale1_compare, 10) + def test_render_scale2(): - #We need png support for these render test + # We need png support for these render test codecs = dir(Image.core) if "zip_encoder" not in codecs or "zip_decoder" not in codecs: skip("zip/deflate support not available") - #Zero bounding box + # Zero bounding box image1_scale2 = Image.open(file1) image1_scale2.load(scale=2) image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB") image1_scale2_compare.load() assert_image_similar(image1_scale2, image1_scale2_compare, 5) - #Non-Zero bounding box + # Non-Zero bounding box image2_scale2 = Image.open(file2) image2_scale2.load(scale=2) image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB") image2_scale2_compare.load() assert_image_similar(image2_scale2, image2_scale2_compare, 10) + + +def test_resize(): + # Arrange + image1 = Image.open(file1) + image2 = Image.open(file2) + new_size = (100, 100) + + # Act + image1 = image1.resize(new_size) + image2 = image1.resize(new_size) + + # Assert + assert_equal(image1.size, new_size) + assert_equal(image2.size, new_size) + + +# End of file From 23eb9e1de87d5c36c46bc59902f4ecc9735b5a79 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 8 May 2014 11:45:02 +0300 Subject: [PATCH 019/168] Thumbnail tests for https://github.com/python-imaging/Pillow/pull/619 --- Tests/test_file_eps.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 9b524e70d..87ec0fe05 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -117,4 +117,19 @@ def test_resize(): assert_equal(image2.size, new_size) +def test_thumbnail(): + # Issue #619 + # Arrange + image1 = Image.open(file1) + image2 = Image.open(file2) + new_size = (100, 100) + + # Act + image1.thumbnail(new_size) + image1.thumbnail(new_size) + + # Assert + assert_equal(image1.size, new_size) + assert_equal(image2.size, new_size) + # End of file From 7a9badf18ed865b6ea3812766a255c7e08209fc2 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 8 May 2014 22:44:46 +0300 Subject: [PATCH 020/168] Fix tests so image2 is resized and thumbnailed --- Tests/test_file_eps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 87ec0fe05..51d5531f2 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -110,7 +110,7 @@ def test_resize(): # Act image1 = image1.resize(new_size) - image2 = image1.resize(new_size) + image2 = image2.resize(new_size) # Assert assert_equal(image1.size, new_size) @@ -126,7 +126,7 @@ def test_thumbnail(): # Act image1.thumbnail(new_size) - image1.thumbnail(new_size) + image2.thumbnail(new_size) # Assert assert_equal(image1.size, new_size) From 11227e5dc107b27919aadfb63b9caa0a392909ac Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 8 May 2014 23:01:44 +0300 Subject: [PATCH 021/168] Fix test because thumbnail() retains aspect ratio --- Tests/test_file_eps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 51d5531f2..c4db7c96a 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -129,7 +129,7 @@ def test_thumbnail(): image2.thumbnail(new_size) # Assert - assert_equal(image1.size, new_size) - assert_equal(image2.size, new_size) + assert_equal(max(image1.size), max(new_size)) + assert_equal(max(image2.size), max(new_size)) # End of file From 22a370afc219af47907b2fe69e791854dd2d9c79 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 10 May 2014 11:46:53 +0300 Subject: [PATCH 022/168] Fix 12-year-old FIXME --- PIL/Image.py | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 333397701..7736a3718 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -101,7 +101,7 @@ import collections import numbers # works everywhere, win for pypy, not cpython -USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') +USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') try: import cffi HAS_CFFI=True @@ -233,7 +233,7 @@ _MODE_CONV = { "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 - # I;16 == I;16L, and I;32 == I;32L + # I;16 == I;16L, and I;32 == I;32L "I;16": ('u2', None), "I;16L": (' 8bit images. + # a gamma function point transform on > 8bit images. scale, offset = _getscaleoffset(lut) return self._new(self.im.point_transform(scale, offset)) # for other modes, convert the function to a table @@ -1420,8 +1420,8 @@ class Image: self._copy() self.pyaccess = None self.load() - - if self.pyaccess: + + if self.pyaccess: return self.pyaccess.putpixel(xy,value) return self.im.putpixel(xy, value) @@ -1667,7 +1667,7 @@ class Image: """ return 0 - def thumbnail(self, size, resample=NEAREST): + def thumbnail(self, size, resample=ANTIALIAS): """ Make this image into a thumbnail. This method modifies the image to contain a thumbnail version of itself, no larger than @@ -1690,14 +1690,10 @@ class Image: of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS` (best quality). If omitted, it defaults to - :py:attr:`PIL.Image.NEAREST` (this will be changed to ANTIALIAS in a - future version). + :py:attr:`PIL.Image.ANTIALIAS`. :returns: None """ - # FIXME: the default resampling filter will be changed - # to ANTIALIAS in future versions - # preserve aspect ratio x, y = self.size if x > size[0]: y = int(max(y * size[0] / x, 1)); x = int(size[0]) From 74514fa1f54d82194d9cb2388f6383b6d8ca8df1 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 10 May 2014 12:34:36 +0300 Subject: [PATCH 023/168] Some pep8 and pyflakes cleanup --- PIL/Image.py | 152 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 94 insertions(+), 58 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 7736a3718..80b7258c6 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -30,6 +30,7 @@ from PIL import VERSION, PILLOW_VERSION, _plugins import warnings + class _imaging_not_installed: # module placeholder def __getattr__(self, id): @@ -52,7 +53,7 @@ try: # directly; import Image and use the Image.core variable instead. from PIL import _imaging as core if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None): - raise ImportError("The _imaging extension was built for another " + raise ImportError("The _imaging extension was built for another " " version of Pillow or PIL") except ImportError as v: @@ -91,10 +92,13 @@ except ImportError: builtins = __builtin__ from PIL import ImageMode -from PIL._binary import i8, o8 -from PIL._util import isPath, isStringType, deferred_error +from PIL._binary import i8 +from PIL._util import isPath +from PIL._util import isStringType +from PIL._util import deferred_error -import os, sys +import os +import sys # type stuff import collections @@ -104,9 +108,10 @@ import numbers USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') try: import cffi - HAS_CFFI=True + HAS_CFFI = True except: - HAS_CFFI=False + HAS_CFFI = False + def isImageType(t): """ @@ -148,16 +153,16 @@ MESH = 4 # resampling filters NONE = 0 NEAREST = 0 -ANTIALIAS = 1 # 3-lobed lanczos +ANTIALIAS = 1 # 3-lobed lanczos LINEAR = BILINEAR = 2 CUBIC = BICUBIC = 3 # dithers NONE = 0 NEAREST = 0 -ORDERED = 1 # Not yet implemented -RASTERIZE = 2 # Not yet implemented -FLOYDSTEINBERG = 3 # default +ORDERED = 1 # Not yet implemented +RASTERIZE = 2 # Not yet implemented +FLOYDSTEINBERG = 3 # default # palettes/quantizers WEB = 0 @@ -222,7 +227,7 @@ else: _MODE_CONV = { # official modes - "1": ('|b1', None), # broken + "1": ('|b1', None), # broken "L": ('|u1', None), "I": (_ENDIAN + 'i4', None), "F": (_ENDIAN + 'f4', None), @@ -232,8 +237,8 @@ _MODE_CONV = { "RGBA": ('|u1', 4), "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), - "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 - # I;16 == I;16L, and I;32 == I;32L + "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 + # I;16 == I;16L, and I;32 == I;32L "I;16": ('u2', None), "I;16L": (' size[0]: y = int(max(y * size[0] / x, 1)); x = int(size[0]) - if y > size[1]: x = int(max(x * size[1] / y, 1)); y = int(size[1]) + if x > size[0]: + y = int(max(y * size[0] / x, 1)) + x = int(size[0]) + if y > size[1]: + x = int(max(x * size[1] / y, 1)) + y = int(size[1]) size = x, y if size == self.size: @@ -1712,7 +1733,7 @@ class Image: except ValueError: if resample != ANTIALIAS: raise - im = self.resize(size, NEAREST) # fallback + im = self.resize(size, NEAREST) # fallback self.im = im.im self.mode = im.mode @@ -1748,7 +1769,9 @@ class Image: """ if self.mode == 'RGBA': - return self.convert('RGBa').transform(size, method, data, resample, fill).convert('RGBA') + return self.convert('RGBa') \ + .transform(size, method, data, resample, fill) \ + .convert('RGBA') if isinstance(method, ImageTransformHandler): return method.transform(size, self, resample=resample, fill=fill) @@ -1795,8 +1818,13 @@ class Image: elif method == QUAD: # quadrilateral warp. data specifies the four corners # given as NW, SW, SE, and NE. - nw = data[0:2]; sw = data[2:4]; se = data[4:6]; ne = data[6:8] - x0, y0 = nw; As = 1.0 / w; At = 1.0 / h + nw = data[0:2] + sw = data[2:4] + se = data[4:6] + ne = data[6:8] + x0, y0 = nw + As = 1.0 / w + At = 1.0 / h data = (x0, (ne[0]-x0)*As, (sw[0]-x0)*At, (se[0]-sw[0]-ne[0]+x0)*As*At, y0, (ne[1]-y0)*As, (sw[1]-y0)*At, @@ -1830,6 +1858,7 @@ class Image: im = self.im.transpose(method) return self._new(im) + # -------------------------------------------------------------------- # Lazy operations @@ -1865,6 +1894,7 @@ class _ImageCrop(Image): # FIXME: future versions should optimize crop/paste # sequences! + # -------------------------------------------------------------------- # Abstract handlers. @@ -1872,10 +1902,12 @@ class ImagePointHandler: # used as a mixin by point transforms (for use with im.point) pass + class ImageTransformHandler: # used as a mixin by geometry transforms (for use with im.transform) pass + # -------------------------------------------------------------------- # Factories @@ -1951,6 +1983,7 @@ def frombytes(mode, size, data, decoder_name="raw", *args): im.frombytes(data, decoder_name, args) return im + def fromstring(*args, **kw): """Deprecated alias to frombytes. @@ -2013,9 +2046,9 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): " frombuffer(mode, size, data, 'raw', mode, 0, 1)", RuntimeWarning, stacklevel=2 ) - args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6 + args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6 if args[0] in _MAPMODES: - im = new(mode, (1,1)) + im = new(mode, (1, 1)) im = im._new( core.map_buffer(data, size, decoder_name, None, 0, args) ) @@ -2135,8 +2168,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): - #import traceback - #traceback.print_exc() + # import traceback + # traceback.print_exc() pass if init(): @@ -2148,13 +2181,14 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): - #import traceback - #traceback.print_exc() + # import traceback + # traceback.print_exc() pass raise IOError("cannot identify image file %r" % (filename if filename else fp)) + # # Image processing. @@ -2253,6 +2287,7 @@ def merge(mode, bands): im.putband(bands[i].im, i) return bands[0]._new(im) + # -------------------------------------------------------------------- # Plugin registry @@ -2311,6 +2346,7 @@ def _show(image, **options): # override me, as necessary _showxv(image, **options) + def _showxv(image, title=None, **options): from PIL import ImageShow ImageShow.show(image, title, **options) From f165d2034fc97f172034cadf8c4b3aa3670ae1f3 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 11 May 2014 09:01:09 +0300 Subject: [PATCH 024/168] Simple test for saving to PDF --- Tests/test_file_pdf.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Tests/test_file_pdf.py diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py new file mode 100644 index 000000000..7519d270f --- /dev/null +++ b/Tests/test_file_pdf.py @@ -0,0 +1,17 @@ +from tester import * +import os.path + + +def test_to_pdf(): + # Arrange + im = lena() + outfile = tempfile("temp.pdf") + + # Act + im.save(outfile) + + # Assert + assert_true(os.path.isfile(outfile)) + assert_greater(os.path.getsize(outfile), 0) + +# End of file From c15601e0b06f5d6ebf28a0f9d6a9721fa54ed235 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 11 May 2014 17:16:13 +0300 Subject: [PATCH 025/168] Add some more pdf tests --- Tests/test_file_pdf.py | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 7519d270f..c9ab8015a 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -2,10 +2,10 @@ from tester import * import os.path -def test_to_pdf(): +def helper_save_as_pdf(mode): # Arrange - im = lena() - outfile = tempfile("temp.pdf") + im = lena(mode) + outfile = tempfile("temp_" + mode + ".pdf") # Act im.save(outfile) @@ -14,4 +14,37 @@ def test_to_pdf(): assert_true(os.path.isfile(outfile)) assert_greater(os.path.getsize(outfile), 0) + +def test_greyscale(): + # Arrange + mode = "L" + + # Act / Assert + helper_save_as_pdf(mode) + + +def test_rgb(): + # Arrange + mode = "RGB" + + # Act / Assert + helper_save_as_pdf(mode) + + +def test_p_mode(): + # Arrange + mode = "P" + + # Act / Assert + helper_save_as_pdf(mode) + + +def test_cmyk_mode(): + # Arrange + mode = "P" + + # Act / Assert + helper_save_as_pdf(mode) + + # End of file From 3e2ff13aa8866af18de64a90e52ee8b7772fd907 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 11 May 2014 17:23:10 +0300 Subject: [PATCH 026/168] Fix CMYK test --- Tests/test_file_pdf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index c9ab8015a..81fd919d2 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -41,7 +41,7 @@ def test_p_mode(): def test_cmyk_mode(): # Arrange - mode = "P" + mode = "CMYK" # Act / Assert helper_save_as_pdf(mode) From 8f1a00ae92a949da0859114a970c04d8b7194bc4 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 11 May 2014 17:28:41 +0300 Subject: [PATCH 027/168] Temporarily remove failing P-mode PDF test --- Tests/test_file_pdf.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 81fd919d2..4bc70d10c 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -31,12 +31,17 @@ def test_rgb(): helper_save_as_pdf(mode) -def test_p_mode(): - # Arrange - mode = "P" - - # Act / Assert - helper_save_as_pdf(mode) +# FIXME: P-mode test fails on Python 3. +# https://travis-ci.org/hugovk/Pillow/builds/24915249 +# File "/home/travis/build/hugovk/Pillow/PIL/PdfImagePlugin.py", line 108, in _save +# colorspace = colorspace + b"> ]" +# TypeError: Can't convert 'bytes' object to str implicitly +# def test_p_mode(): +# # Arrange +# mode = "P" +# +# # Act / Assert +# helper_save_as_pdf(mode) def test_cmyk_mode(): From 6520004289648e3611d43fab7190751f18912aef Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 11 May 2014 17:35:49 +0300 Subject: [PATCH 028/168] Add monochrome PDF test --- Tests/test_file_pdf.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 4bc70d10c..ed6b50b5d 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -15,6 +15,14 @@ def helper_save_as_pdf(mode): assert_greater(os.path.getsize(outfile), 0) +def test_monochrome(): + # Arrange + mode = "1" + + # Act / Assert + helper_save_as_pdf(mode) + + def test_greyscale(): # Arrange mode = "L" @@ -33,9 +41,11 @@ def test_rgb(): # FIXME: P-mode test fails on Python 3. # https://travis-ci.org/hugovk/Pillow/builds/24915249 -# File "/home/travis/build/hugovk/Pillow/PIL/PdfImagePlugin.py", line 108, in _save +# File "/home/travis/build/hugovk/Pillow/PIL/PdfImagePlugin.py", line 108, +# in _save # colorspace = colorspace + b"> ]" # TypeError: Can't convert 'bytes' object to str implicitly + # def test_p_mode(): # # Arrange # mode = "P" From 741297326a030372fd8dfb6dc6e0a2ecc6dba2f8 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 11 May 2014 17:41:07 +0300 Subject: [PATCH 029/168] Temporarily remove 'failing' 1-mode PDF test --- Tests/test_file_pdf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index ed6b50b5d..ddecdd75f 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -15,6 +15,12 @@ def helper_save_as_pdf(mode): assert_greater(os.path.getsize(outfile), 0) +# FIXME: 1-mode test "fails" because it produces a warning. +# https://travis-ci.org/hugovk/Pillow/builds/24916085 +# /home/travis/build/hugovk/Pillow/PIL/PdfImagePlugin.py:147: +# DeprecationWarning: tostring() is deprecated. Please call tobytes() instead. +# data = im.tostring("raw", "1") + def test_monochrome(): # Arrange mode = "1" From bcb8534dcf8ad5e8b10eece136f18062818b7071 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sun, 11 May 2014 17:46:13 +0300 Subject: [PATCH 030/168] Temporarily remove 'failing' 1-mode PDF test --- Tests/test_file_pdf.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index ddecdd75f..9a4faff3f 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -20,13 +20,13 @@ def helper_save_as_pdf(mode): # /home/travis/build/hugovk/Pillow/PIL/PdfImagePlugin.py:147: # DeprecationWarning: tostring() is deprecated. Please call tobytes() instead. # data = im.tostring("raw", "1") - -def test_monochrome(): - # Arrange - mode = "1" - - # Act / Assert - helper_save_as_pdf(mode) +# +# def test_monochrome(): +# # Arrange +# mode = "1" +# +# # Act / Assert +# helper_save_as_pdf(mode) def test_greyscale(): @@ -51,7 +51,7 @@ def test_rgb(): # in _save # colorspace = colorspace + b"> ]" # TypeError: Can't convert 'bytes' object to str implicitly - +# # def test_p_mode(): # # Arrange # mode = "P" From c37aa0a9ca0db921e62ae289f7bc357b7a5aef55 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 12 May 2014 14:30:03 +0300 Subject: [PATCH 031/168] Fix tostring()/tobytes() warning and reinstate test --- PIL/PdfImagePlugin.py | 2 +- Tests/test_file_pdf.py | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 725f22ecf..2a8a8d443 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -144,7 +144,7 @@ def _save(im, fp, filename): if bits == 1: # FIXME: the hex encoder doesn't support packed 1-bit # images; do things the hard way... - data = im.tostring("raw", "1") + data = im.tobytes("raw", "1") im = Image.new("L", (len(data), 1), None) im.putdata(data) ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)]) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 9a4faff3f..e31878aef 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -15,18 +15,12 @@ def helper_save_as_pdf(mode): assert_greater(os.path.getsize(outfile), 0) -# FIXME: 1-mode test "fails" because it produces a warning. -# https://travis-ci.org/hugovk/Pillow/builds/24916085 -# /home/travis/build/hugovk/Pillow/PIL/PdfImagePlugin.py:147: -# DeprecationWarning: tostring() is deprecated. Please call tobytes() instead. -# data = im.tostring("raw", "1") -# -# def test_monochrome(): -# # Arrange -# mode = "1" -# -# # Act / Assert -# helper_save_as_pdf(mode) +def test_monochrome(): + # Arrange + mode = "1" + + # Act / Assert + helper_save_as_pdf(mode) def test_greyscale(): From 8cda5170c8f1c60f5537b623883ef6a9436f2df6 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 12 May 2014 14:45:54 +0300 Subject: [PATCH 032/168] Fix bytes/str and reinstate test --- PIL/PdfImagePlugin.py | 2 +- Tests/test_file_pdf.py | 19 ++++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 2a8a8d443..c89ca3ee0 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -105,7 +105,7 @@ def _save(im, fp, filename): g = i8(palette[i*3+1]) b = i8(palette[i*3+2]) colorspace = colorspace + "%02x%02x%02x " % (r, g, b) - colorspace = colorspace + b"> ]" + colorspace = colorspace + "> ]" procset = "/ImageI" # indexed color elif im.mode == "RGB": filter = "/DCTDecode" diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index e31878aef..e99f22db1 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -39,19 +39,12 @@ def test_rgb(): helper_save_as_pdf(mode) -# FIXME: P-mode test fails on Python 3. -# https://travis-ci.org/hugovk/Pillow/builds/24915249 -# File "/home/travis/build/hugovk/Pillow/PIL/PdfImagePlugin.py", line 108, -# in _save -# colorspace = colorspace + b"> ]" -# TypeError: Can't convert 'bytes' object to str implicitly -# -# def test_p_mode(): -# # Arrange -# mode = "P" -# -# # Act / Assert -# helper_save_as_pdf(mode) +def test_p_mode(): + # Arrange + mode = "P" + + # Act / Assert + helper_save_as_pdf(mode) def test_cmyk_mode(): From 3ff73688fe12373e4a4d1996962bf107b38a5e89 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 12 May 2014 14:56:55 +0300 Subject: [PATCH 033/168] pep8 and pyflakes --- PIL/PdfImagePlugin.py | 76 ++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index c89ca3ee0..fcc841438 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -46,9 +46,11 @@ def _obj(fp, obj, **dict): fp.write("/%s %s\n" % (k, v)) fp.write(">>\n") + def _endobj(fp): fp.write("endobj\n") + ## # (Internal) Image save plugin for the PDF format. @@ -59,13 +61,15 @@ def _save(im, fp, filename): # make sure image data is available im.load() - xref = [0]*(5+1) # placeholders + xref = [0]*(5+1) # placeholders class TextWriter: def __init__(self, fp): self.fp = fp + def __getattr__(self, name): return getattr(self.fp, name) + def write(self, value): self.fp.write(value.encode('latin-1')) @@ -89,13 +93,13 @@ def _save(im, fp, filename): if im.mode == "1": filter = "/ASCIIHexDecode" colorspace = "/DeviceGray" - procset = "/ImageB" # grayscale + procset = "/ImageB" # grayscale bits = 1 elif im.mode == "L": filter = "/DCTDecode" # params = "<< /Predictor 15 /Columns %d >>" % (width-2) colorspace = "/DeviceGray" - procset = "/ImageB" # grayscale + procset = "/ImageB" # grayscale elif im.mode == "P": filter = "/ASCIIHexDecode" colorspace = "[ /Indexed /DeviceRGB 255 <" @@ -106,15 +110,15 @@ def _save(im, fp, filename): b = i8(palette[i*3+2]) colorspace = colorspace + "%02x%02x%02x " % (r, g, b) colorspace = colorspace + "> ]" - procset = "/ImageI" # indexed color + procset = "/ImageI" # indexed color elif im.mode == "RGB": filter = "/DCTDecode" colorspace = "/DeviceRGB" - procset = "/ImageC" # color images + procset = "/ImageC" # color images elif im.mode == "CMYK": filter = "/DCTDecode" colorspace = "/DeviceCMYK" - procset = "/ImageC" # color images + procset = "/ImageC" # color images else: raise ValueError("cannot save mode %s" % im.mode) @@ -122,17 +126,21 @@ def _save(im, fp, filename): # catalogue xref[1] = fp.tell() - _obj(fp, 1, Type = "/Catalog", - Pages = "2 0 R") + _obj( + fp, 1, + Type="/Catalog", + Pages="2 0 R") _endobj(fp) # # pages xref[2] = fp.tell() - _obj(fp, 2, Type = "/Pages", - Count = 1, - Kids = "[4 0 R]") + _obj( + fp, 2, + Type="/Pages", + Count=1, + Kids="[4 0 R]") _endobj(fp) # @@ -147,26 +155,28 @@ def _save(im, fp, filename): data = im.tobytes("raw", "1") im = Image.new("L", (len(data), 1), None) im.putdata(data) - ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)]) + ImageFile._save(im, op, [("hex", (0, 0)+im.size, 0, im.mode)]) elif filter == "/DCTDecode": Image.SAVE["JPEG"](im, op, filename) elif filter == "/FlateDecode": - ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)]) + ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)]) elif filter == "/RunLengthDecode": - ImageFile._save(im, op, [("packbits", (0,0)+im.size, 0, im.mode)]) + ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)]) else: raise ValueError("unsupported PDF filter (%s)" % filter) xref[3] = fp.tell() - _obj(fp, 3, Type = "/XObject", - Subtype = "/Image", - Width = width, # * 72.0 / resolution, - Height = height, # * 72.0 / resolution, - Length = len(op.getvalue()), - Filter = filter, - BitsPerComponent = bits, - DecodeParams = params, - ColorSpace = colorspace) + _obj( + fp, 3, + Type="/XObject", + Subtype="/Image", + Width=width, # * 72.0 / resolution, + Height=height, # * 72.0 / resolution, + Length=len(op.getvalue()), + Filter=filter, + BitsPerComponent=bits, + DecodeParams=params, + ColorSpace=colorspace) fp.write("stream\n") fp.fp.write(op.getvalue()) @@ -179,11 +189,14 @@ def _save(im, fp, filename): xref[4] = fp.tell() _obj(fp, 4) - fp.write("<<\n/Type /Page\n/Parent 2 0 R\n"\ - "/Resources <<\n/ProcSet [ /PDF %s ]\n"\ - "/XObject << /image 3 0 R >>\n>>\n"\ - "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\ - (procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution))) + fp.write( + "<<\n/Type /Page\n/Parent 2 0 R\n" + "/Resources <<\n/ProcSet [ /PDF %s ]\n" + "/XObject << /image 3 0 R >>\n>>\n" + "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" % ( + procset, + int(width * 72.0 / resolution), + int(height * 72.0 / resolution))) _endobj(fp) # @@ -191,10 +204,13 @@ def _save(im, fp, filename): op = TextWriter(io.BytesIO()) - op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) + op.write( + "q %d 0 0 %d 0 0 cm /image Do Q\n" % ( + int(width * 72.0 / resolution), + int(height * 72.0 / resolution))) xref[5] = fp.tell() - _obj(fp, 5, Length = len(op.fp.getvalue())) + _obj(fp, 5, Length=len(op.fp.getvalue())) fp.write("stream\n") fp.fp.write(op.fp.getvalue()) From 92eea7a9cc5022750e82ba07da96612bdea03f72 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 12 May 2014 17:32:04 +0300 Subject: [PATCH 034/168] Tests for ImageDraw --- Tests/images/imagedraw_arc.png | Bin 0 -> 284 bytes Tests/images/imagedraw_chord.png | Bin 0 -> 326 bytes Tests/images/imagedraw_ellipse.png | Bin 0 -> 491 bytes Tests/images/imagedraw_pieslice.png | Bin 0 -> 405 bytes Tests/test_imagedraw.py | 106 ++++++++++++++++++++++++++++ 5 files changed, 106 insertions(+) create mode 100644 Tests/images/imagedraw_arc.png create mode 100644 Tests/images/imagedraw_chord.png create mode 100644 Tests/images/imagedraw_ellipse.png create mode 100644 Tests/images/imagedraw_pieslice.png diff --git a/Tests/images/imagedraw_arc.png b/Tests/images/imagedraw_arc.png new file mode 100644 index 0000000000000000000000000000000000000000..b097743890cb1907122e53a98231e8887d791d12 GIT binary patch literal 284 zcmeAS@N?(olHy`uVBq!ia0vp^DIm3c2+)C>xpS`>3f5(Zs9FD75X*zkc7DBDk+omSCdxl;y1HgSsJzU zmEpZza$2#*Ruz7e8#YW!Ufg!%%NmQF_u8+!e@Xt)YcH*9vF6qLSDa@!ATD08i}~^A Vp2dg61W$kjJYD@<);T3K0RX^ebT$A0 literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_chord.png b/Tests/images/imagedraw_chord.png new file mode 100644 index 0000000000000000000000000000000000000000..db3b35310235b1c7ab6e368d08f3bf89b9836777 GIT binary patch literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^DImUz33VYkl1=nX|dqBVT;otL=PqD@Cub z;$2<(Q7d|t+$rnTn(Kcp*>>*yf#>4CDh^JY@$Oe#xmVO0!f&cvEFGQ N0Z&&ymvv4FO#l*njdTD2 literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_ellipse.png b/Tests/images/imagedraw_ellipse.png new file mode 100644 index 0000000000000000000000000000000000000000..fb03fd148597a95bd4c446243b064e91561973ff GIT binary patch literal 491 zcmeAS@N?(olHy`uVBq!ia0vp^DImVh<$#e#iZH>9#NfrDIbrR>YpU ze7$YWlI^<={o3`zoN@0bj&rel?AHhzp0!dkZr|!Ur!TXt|4fOf{JhIYI({vYbH8n~ zc*b*c!6RReTsp#|>pefj$JfVGcADzSxpw-g5y}5e?m2%e=DqrGY51nd$yPnSC9%7v zEtR#4I=7B9`^|jce)dfVy-RF;Wee0^^mI+TdYdu*=+ur~iRGIwT?vrAreSw4a`C5x zryunVqRx7`>s@dDGT$MBb8F(-V);2!LqqKjtT#z;Ewn8;%yP&}VGRnQF@K7lS*QNm zSdk>^3}sWN-MWEb#G4)TthcJgG5d9|o^KVGt;09S8ivU?I=I@PlRl V%h(%|l_|GCVxF#kF6*2UngEBh-Om63 literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_pieslice.png b/Tests/images/imagedraw_pieslice.png new file mode 100644 index 0000000000000000000000000000000000000000..1b2acff92ab9aa263ce85ceb1a37493d4cbef6cb GIT binary patch literal 405 zcmeAS@N?(olHy`uVBq!ia0vp^DImmi_QNfYxaH53Vj{XAGUgN#YVIFC)qlsvNwOXirup8(c8%OTW-&O7bl%x zb9aX7OqFSEUrq^oubqDX-&Cz>|MDXD^3Br?du+5*=-8*|9?s;GxrWbru1ff?+p^I{ zuQM|_H>Z5vnT=<_gy8GO{EpOZxvKrC)xJJ*r^PnPDO_VdQk8HsVzb8!n${V^hH)%9N+zbw06{%>NP@%}SSuANIouO%;tm~~G(Ovm Date: Mon, 12 May 2014 19:55:18 +0300 Subject: [PATCH 035/168] Make sure bounding box in ints (for Py3) --- Tests/test_imagedraw.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 11f4fcd91..9904457af 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -7,10 +7,10 @@ from PIL import ImageDraw w, h = 100, 100 # Bounding box points -x0 = w / 4 -x1 = x0 * 3 -y0 = h / 4 -y1 = x0 * 3 +x0 = int(w / 4) +x1 = int(x0 * 3) +y0 = int(h / 4) +y1 = int(x0 * 3) # Two kinds of bounding box bbox1 = [(x0, y0), (x1, y1)] From 6b274b4c5e22a06ad72a682f7cb80a8b541dd87b Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 12 May 2014 21:45:16 +0300 Subject: [PATCH 036/168] More ImageDraw tests. Some may need redoing after issues #367 and #463 are sorted. --- Tests/images/imagedraw_line.png | Bin 0 -> 286 bytes Tests/images/imagedraw_point.png | Bin 0 -> 124 bytes Tests/images/imagedraw_polygon.png | Bin 0 -> 292 bytes Tests/images/imagedraw_rectangle.png | Bin 0 -> 228 bytes Tests/test_imagedraw.py | 88 +++++++++++++++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 Tests/images/imagedraw_line.png create mode 100644 Tests/images/imagedraw_point.png create mode 100644 Tests/images/imagedraw_polygon.png create mode 100644 Tests/images/imagedraw_rectangle.png diff --git a/Tests/images/imagedraw_line.png b/Tests/images/imagedraw_line.png new file mode 100644 index 0000000000000000000000000000000000000000..6d0e6994d861246c94a905d395b683775489ed09 GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^DIm(?a5DGbZ$zeim|1j!c@7cxq%qP7CDP~|u_;jC{;ef806_|%a9N^|HJI(rAL?`wsNYvBS&t;uc GLK6VpyB)Xy literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_polygon.png b/Tests/images/imagedraw_polygon.png new file mode 100644 index 0000000000000000000000000000000000000000..5f160be76ceef3d85c7092a63d87e48115ec3303 GIT binary patch literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^DImw+vGjmJT^EHnX^geT_FfuUkUHZNI{sZ}!Rk|0m4{9vEKINY9-e)hTs>plK zf0wzoVExtz&HLGxuB(RaJN`74*Yta-VVKvqS^Za|;!cOWF0GApUKv-OyfkS2(un&p zj{=rlZ%gGBt;+45%Ct8u`ENz%;hTHQ^s+5>T>lqgxjlUMmyq|u{M){)zW?9dcGH?U z5nB95tu}?MJNE0X`Nu0kH}|b+_D;=Tv)TLZmtXf^?!N!LuzFf(O0aa2sAg&W`hwcs f8mvISp@4!0|C_PVOkF`octGNwu6{1-oD!M-=- z>O8hT9}YG)8MEq~w&nw>0Rg?=IellJ-B`Bs_km6zUKttvYn|@ihKNQQ!h`vh`P05b c>}QaB%D Date: Mon, 12 May 2014 23:27:02 +0300 Subject: [PATCH 037/168] Test experimental floodfill() --- Tests/images/imagedraw_floodfill.png | Bin 0 -> 232 bytes Tests/test_imagedraw.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 Tests/images/imagedraw_floodfill.png diff --git a/Tests/images/imagedraw_floodfill.png b/Tests/images/imagedraw_floodfill.png new file mode 100644 index 0000000000000000000000000000000000000000..89376a0f01585431b6dfcdce1ab78d41ad1ae0c0 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^DIm>$A6c=E^p z_-RJRvt}Gvu!@~0CVtOxMxZJnIFS6j;PtMyWbfxMrrtk$=Dg9r6Ysd~H!l?z(55Af ikWc^j+E5$nh9kTQ1uU8CSMGTP67Y2Ob6Mw<&;$VOggeUs literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 727275c2d..73617b97a 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1,6 +1,7 @@ from tester import * from PIL import Image +from PIL import ImageColor from PIL import ImageDraw # Image size @@ -219,4 +220,19 @@ def test_rectangle2(): helper_rectangle(bbox2) +def test_floodfill(): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + draw.rectangle(bbox2, outline="yellow", fill="green") + centre_point = (int(w/2), int(h/2)) + + # Act + ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill.png")) + + # End of file From 5b55cb72d33c692f1677341311ec3df05dd0b500 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 12 May 2014 23:33:05 +0300 Subject: [PATCH 038/168] Test experimental floodfill with a border --- Tests/images/imagedraw_floodfill2.png | Bin 0 -> 212 bytes Tests/test_imagedraw.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 Tests/images/imagedraw_floodfill2.png diff --git a/Tests/images/imagedraw_floodfill2.png b/Tests/images/imagedraw_floodfill2.png new file mode 100644 index 0000000000000000000000000000000000000000..41b92fb75a032515147d1d63d27f45d61d85f69b GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^DIm?LEc!(!8PgVbG9L%=Jzf1= J);T3K0RX!!He~<+ literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 73617b97a..2a1f963c7 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -235,4 +235,21 @@ def test_floodfill(): assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill.png")) +def test_floodfill_border(): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + draw.rectangle(bbox2, outline="yellow", fill="green") + centre_point = (int(w/2), int(h/2)) + + # Act + ImageDraw.floodfill( + im, centre_point, ImageColor.getrgb("red"), + border=ImageColor.getrgb("black")) + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) + + # End of file From 60b25701332bb2056e3903761f456849186f046f Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 13 May 2014 14:43:48 +0300 Subject: [PATCH 039/168] Test ImageDraw's bitmap() --- Tests/images/imagedraw_bitmap.png | Bin 0 -> 2127 bytes Tests/test_imagedraw.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 Tests/images/imagedraw_bitmap.png diff --git a/Tests/images/imagedraw_bitmap.png b/Tests/images/imagedraw_bitmap.png new file mode 100644 index 0000000000000000000000000000000000000000..05337b693928aa27ede94ddf8aa1b4afc534ff8c GIT binary patch literal 2127 zcmaKudr;Eb7RP^@CC6j=2+6DFBPgwlMoLPC*~KvN_(%>Tzgi>yS|CmJzIj7)3jly1xD)?4opSFZ%QtGL$W(A4S>o-O&f52I zJLT~to9+TKdYxQWVhSnOj}3*RMfSv zA}P78VqCOQF|G(jOiQIP6HZT*!n7Ts$w>}Jzb!e4(JHjY4GjB~4Or*qe}wM3JV7{j z4!!Vh(RB8a#Uxe!NxR$ZJA$uyH6?iG&kLV@O3CX7iT_l({La1S0dww9_2lSHzfhrR zr_ko@aACYzUcB_=h!KCyK0VfRPF!8!UR&q^Guv_5On}hE3?Ottp`GW0z;%I5f6IQy zzm%*!YQ*0^dhm{O^3t2-DM7%)(wu*iuY zgz3Mpt*t4gt-;bKl(XPI|A(iWM!Y0zPS?!0orDJ3m9mxG)>Tj<6q>$ry*+K?c@j8IPmnb7EPLe0KxPbdlZ{=UhV7T<6uV zoKAA9&6R|NBI8|{0WRELIA9&`)e?|NCSGUG9yKYRqqYp~bg*iG3yTPY@%Kn+>wSz} z`Y#hY;6iSFv_1yJZfq1qO1N-o;%K;VN=4Lk&zB|XmFYGx76&tIX?JmOxO~IO6^(AT(V@Mr4rt!#Z20$b zFJAMcq`|kq+{O7tu}>8mHyd=UT8b3qFy6+^+fEXkNM=m-ZVJekMQ)A;?_u3-ZjEd?99&~)b2uB&GpKkWrH$|-?6C2fG zqPq=RIFN7Zgy>hB!jUTVUmmx7EtN_Y3JcwBztjycEW|ig;PE4R>~Vu!tm6&l)a~%R zWp!Q}K!JrTk0Mh&KW;fmt>P$eVyr4ek!?h*-atKrQc_Zyy>@pY0n$H)$UA3OCZ3CZa;9xOuP-bsE6dHz zEz@#mmL#3Yn^MTUjdBp#rLb)1urjYY-5;BYvK38laP@%2;J z_4Na|qA66SrPizz9na<2)VcW)87eC&?zzjUs|n)rBmMc9`Q4Gpzj5NiU%^DD!{ z!{?o#G)r2EXWf|=4CqilpW_B16ZhT3%6PZ~BYHlam0PkA>TAhla!|Nq#a^l(@IsBP zi@2h?TG6DvvgQ4!#9)P7{%THD=RepkTg=MEW>vpMq?}-AL)BbMYpZMe#`DA& z84rdl@thefwhjmgVAZ>%d=B{vB?ju#QqXoFO+oy}GDQD<`&lvl%E_?8KYQOkOSU+c z7RA~pRsn$F`2%!yZyBK6ms<-H?8?rPNL`obnCc3I1$QLOY?=>pGcE7?Ka|Vm$3xmB zFkzkIxe*)s*keRFFKIBI8XqSDKs=IFKajmdN#?rSHfv~J4NX?#+`=CRM z^|Um*LmsuaJDKFKuB;GB6Yq9Cj$MS>U_txo{x)Dl|47V8;@b%OTZfW@r7E;xj2dvy zIt;!3e&L-;4R!0J*F5d$DEF+>%mBuF5xOdi6}6vv@wG!X5C%s%s!XKa<|xes^L(GQ z(kaxJRF`UT@zKEvUl^1mMYa&&?zMiwQd{AHGGSxmcf^S?#(F2dYVK-kDxyE)B&NpG zFn0@Joc)e^%)DR=Fwm8oL{Xxcu_ck-l0T5%czm#Q6VkbA4Jkj|HTK3{*A3IqnUrKE zSVmibv<5cw)1GRq>`pJNuKC}{{qN-cKawNf1ahkTB$z@01oSOf`bXk{5P{v B0-gW> literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 2a1f963c7..0a9366928 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -71,6 +71,20 @@ def test_arc2(): helper_arc(bbox2) +def test_bitmap(): + # Arrange + small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.bitmap((10, 10), small) + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png")) + + def helper_chord(bbox): # Arrange im = Image.new("RGB", (w, h)) From 41d3c386a983f3ea716c4a06f83a919485d22615 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 14 May 2014 08:17:49 +0300 Subject: [PATCH 040/168] Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) --- .travis.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ae6e415c..96c10d0ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,12 +4,12 @@ notifications: irc: "chat.freenode.net#pil" python: - - "pypy" - 2.6 - 2.7 - 3.2 - 3.3 - 3.4 + - "pypy" install: - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" @@ -26,8 +26,14 @@ script: - coverage erase - python setup.py clean - python setup.py build_ext --inplace - - coverage run --append --include=PIL/* selftest.py - - python Tests/run.py --coverage + + # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi + + # Cover the others + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi after_success: - coverage report @@ -37,7 +43,3 @@ after_success: - pyflakes PIL/*.py - pep8 Tests/*.py - pyflakes Tests/*.py - -matrix: - allow_failures: - - python: "pypy" From 6ca9e9d70383a5be60a69387f99bb08b6847e280 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 14 May 2014 08:47:13 +0300 Subject: [PATCH 041/168] Keep PyPy first, it's still a little slower --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 96c10d0ae..36dae5b7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,12 +4,12 @@ notifications: irc: "chat.freenode.net#pil" python: + - "pypy" - 2.6 - 2.7 - 3.2 - 3.3 - 3.4 - - "pypy" install: - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" From a712cec02baedc80a93b04bbad8fc6513ae763c7 Mon Sep 17 00:00:00 2001 From: eliempje Date: Wed, 14 May 2014 23:14:55 +0200 Subject: [PATCH 042/168] Added test and testfile for issue #302 --- Tests/images/binary_preview_map.eps | Bin 0 -> 843167 bytes Tests/test_file_eps.py | 7 +++++++ 2 files changed, 7 insertions(+) create mode 100755 Tests/images/binary_preview_map.eps diff --git a/Tests/images/binary_preview_map.eps b/Tests/images/binary_preview_map.eps new file mode 100755 index 0000000000000000000000000000000000000000..d0a4204aac4bf56d53504daa4ad5107d59f3bb13 GIT binary patch literal 843167 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index c4db7c96a..34ece8c8b 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -17,6 +17,8 @@ file1_compare_scale2 = "Tests/images/zero_bb_scale2.png" file2_compare = "Tests/images/non_zero_bb.png" file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png" +# EPS test files with binary preview +file3 = "Tests/images/binary_preview_map.eps" def test_sanity(): # Regular scale @@ -132,4 +134,9 @@ def test_thumbnail(): assert_equal(max(image1.size), max(new_size)) assert_equal(max(image2.size), max(new_size)) +def test_read_binary_preview(): + # Issue 302 + # open image with binary preview + image1 = Image.open(file3) + # End of file From 55b1accb42a5b2d4e1372546629ff06024863915 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 17 May 2014 19:08:08 +0300 Subject: [PATCH 043/168] Add (failing) test for #664 --- Tests/test_image_convert.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 4d40e43b2..6f170afe2 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -45,7 +45,7 @@ def test_16bit(): def test_16bit_workaround(): im = Image.open('Tests/images/16bit.cropped.tif') _test_float_conversion(im.convert('I')) - + def test_rgba_p(): im = lena('RGBA') im.putalpha(lena('L')) @@ -54,8 +54,8 @@ def test_rgba_p(): comparable = converted.convert('RGBA') assert_image_similar(im, comparable, 20) - -def test_trns_p(): + +def test_trns_p(): im = lena('P') im.info['transparency']=0 @@ -69,13 +69,26 @@ def test_trns_p(): rgb = im.convert('RGB') assert_equal(rgb.info['transparency'], (0,0,0)) # undone assert_no_exception(lambda: rgb.save(f)) - + + +def test_trns_p_rgba(): + # Arrange + im = lena('P') + im.info['transparency'] = 128 + + # Act + rgba = im.convert('RGBA') + + # Assert + assert_false('transparency' in rgba.info) + + def test_trns_l(): im = lena('L') im.info['transparency'] = 128 f = tempfile('temp.png') - + rgb = im.convert('RGB') assert_equal(rgb.info['transparency'], (128,128,128)) # undone assert_no_exception(lambda: rgb.save(f)) @@ -89,13 +102,13 @@ def test_trns_l(): assert_false('transparency' in p.info) assert_no_exception(lambda: p.save(f)) - + def test_trns_RGB(): im = lena('RGB') im.info['transparency'] = im.getpixel((0,0)) f = tempfile('temp.png') - + l = im.convert('L') assert_equal(l.info['transparency'], l.getpixel((0,0))) # undone assert_no_exception(lambda: l.save(f)) @@ -103,7 +116,7 @@ def test_trns_RGB(): p = im.convert('P') assert_true('transparency' in p.info) assert_no_exception(lambda: p.save(f)) - + p = assert_warning(UserWarning, lambda: im.convert('P', palette = Image.ADAPTIVE)) assert_false('transparency' in p.info) From ca7608f1d5a0d4322d185da6607fa1db2b418c0f Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 17 May 2014 20:36:17 +0300 Subject: [PATCH 044/168] Possible fix for #664 --- PIL/Image.py | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 333397701..ec25f1027 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -101,7 +101,7 @@ import collections import numbers # works everywhere, win for pypy, not cpython -USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') +USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info') try: import cffi HAS_CFFI=True @@ -233,7 +233,7 @@ _MODE_CONV = { "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 - # I;16 == I;16L, and I;32 == I;32L + # I;16 == I;16L, and I;32 == I;32L "I;16": ('u2', None), "I;16L": (' 8bit images. + # a gamma function point transform on > 8bit images. scale, offset = _getscaleoffset(lut) return self._new(self.im.point_transform(scale, offset)) # for other modes, convert the function to a table @@ -1420,8 +1423,8 @@ class Image: self._copy() self.pyaccess = None self.load() - - if self.pyaccess: + + if self.pyaccess: return self.pyaccess.putpixel(xy,value) return self.im.putpixel(xy, value) From a59bc40c38661c92748d8be93ca2e03bdb01f613 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 17 May 2014 23:33:50 +0300 Subject: [PATCH 045/168] flake8 --- Tests/test_image_convert.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 6f170afe2..6a39b0e3b 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -2,6 +2,7 @@ from tester import * from PIL import Image + def test_sanity(): def convert(im, mode): @@ -16,6 +17,7 @@ def test_sanity(): for mode in modes: yield_test(convert, im, mode) + def test_default(): im = lena("P") @@ -26,26 +28,29 @@ def test_default(): assert_image(im, "RGB", im.size) - # ref https://github.com/python-imaging/Pillow/issues/274 def _test_float_conversion(im): - orig = im.getpixel((5,5)) - converted = im.convert('F').getpixel((5,5)) + orig = im.getpixel((5, 5)) + converted = im.convert('F').getpixel((5, 5)) assert_equal(orig, converted) + def test_8bit(): im = Image.open('Images/lena.jpg') _test_float_conversion(im.convert('L')) + def test_16bit(): im = Image.open('Tests/images/16bit.cropped.tif') _test_float_conversion(im) + def test_16bit_workaround(): im = Image.open('Tests/images/16bit.cropped.tif') _test_float_conversion(im.convert('I')) + def test_rgba_p(): im = lena('RGBA') im.putalpha(lena('L')) @@ -55,22 +60,24 @@ def test_rgba_p(): assert_image_similar(im, comparable, 20) + def test_trns_p(): im = lena('P') - im.info['transparency']=0 + im.info['transparency'] = 0 f = tempfile('temp.png') l = im.convert('L') - assert_equal(l.info['transparency'], 0) # undone + assert_equal(l.info['transparency'], 0) # undone assert_no_exception(lambda: l.save(f)) - rgb = im.convert('RGB') - assert_equal(rgb.info['transparency'], (0,0,0)) # undone + assert_equal(rgb.info['transparency'], (0, 0, 0)) # undone assert_no_exception(lambda: rgb.save(f)) +# ref https://github.com/python-imaging/Pillow/issues/664 + def test_trns_p_rgba(): # Arrange im = lena('P') @@ -90,7 +97,7 @@ def test_trns_l(): f = tempfile('temp.png') rgb = im.convert('RGB') - assert_equal(rgb.info['transparency'], (128,128,128)) # undone + assert_equal(rgb.info['transparency'], (128, 128, 128)) # undone assert_no_exception(lambda: rgb.save(f)) p = im.convert('P') @@ -98,19 +105,19 @@ def test_trns_l(): assert_no_exception(lambda: p.save(f)) p = assert_warning(UserWarning, - lambda: im.convert('P', palette = Image.ADAPTIVE)) + lambda: im.convert('P', palette=Image.ADAPTIVE)) assert_false('transparency' in p.info) assert_no_exception(lambda: p.save(f)) def test_trns_RGB(): im = lena('RGB') - im.info['transparency'] = im.getpixel((0,0)) + im.info['transparency'] = im.getpixel((0, 0)) f = tempfile('temp.png') l = im.convert('L') - assert_equal(l.info['transparency'], l.getpixel((0,0))) # undone + assert_equal(l.info['transparency'], l.getpixel((0, 0))) # undone assert_no_exception(lambda: l.save(f)) p = im.convert('P') @@ -118,8 +125,6 @@ def test_trns_RGB(): assert_no_exception(lambda: p.save(f)) p = assert_warning(UserWarning, - lambda: im.convert('P', palette = Image.ADAPTIVE)) + lambda: im.convert('P', palette=Image.ADAPTIVE)) assert_false('transparency' in p.info) assert_no_exception(lambda: p.save(f)) - - From 0938145e57e6d4e838f02495da80e03fd2a4cbeb Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 19 May 2014 10:04:56 +0300 Subject: [PATCH 046/168] Move to an elif clause of the if in line 778, as it's just another combination of mode,self.mode under the master transparency condition --- PIL/Image.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index ec25f1027..5d23a278a 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -802,7 +802,8 @@ class Image: # after quantization. trns_im = trns_im.convert('RGB') trns = trns_im.getpixel((0,0)) - + elif self.mode == 'P' and mode == 'RGBA' and 'transparency' in self.info: + delete_trns = True if mode == "P" and palette == ADAPTIVE: im = self.im.quantize(colors) @@ -837,9 +838,6 @@ class Image: except KeyError: raise ValueError("illegal conversion") - if self.mode == 'P' and mode == 'RGBA' and 'transparency' in self.info: - delete_trns = True - new_im = self._new(im) if delete_trns: #crash fail if we leave a bytes transparency in an rgb/l mode. From 88d74883bb507ca42a5f203e46b0ada8a47fc1cc Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 19 May 2014 14:12:43 -0700 Subject: [PATCH 047/168] remove redundant transparency check --- PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 5d23a278a..08b0dbe7d 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -802,7 +802,7 @@ class Image: # after quantization. trns_im = trns_im.convert('RGB') trns = trns_im.getpixel((0,0)) - elif self.mode == 'P' and mode == 'RGBA' and 'transparency' in self.info: + elif self.mode == 'P' and mode == 'RGBA': delete_trns = True if mode == "P" and palette == ADAPTIVE: From a7cb81ec56107234b012e2f2859a53122045f77b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 20 May 2014 16:42:18 +0100 Subject: [PATCH 048/168] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b6eb19b3a..8121a9519 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Remove transparency resource after P->RGBA conversion + [hugovk] + - Clean up preprocessor cruft for Windows [CounterPillow] From 9a6e8f8e9dca4587a232bf5c4395b1cad782208b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 20 May 2014 09:21:12 -0700 Subject: [PATCH 049/168] Noted previous default --- PIL/Image.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index c6ab76b6d..ec11a1866 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1710,7 +1710,8 @@ class Image: of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS` (best quality). If omitted, it defaults to - :py:attr:`PIL.Image.ANTIALIAS`. + :py:attr:`PIL.Image.ANTIALIAS`. (was `PIL.Image.NEAREST` prior + to version 2.5.0) :returns: None """ From 6ab636fb5ac17bc844400113e69e9c0ee5cf7853 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 20 May 2014 09:27:56 -0700 Subject: [PATCH 050/168] Doc formatting fix --- PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index ec11a1866..2ef226993 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1710,8 +1710,8 @@ class Image: of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS` (best quality). If omitted, it defaults to - :py:attr:`PIL.Image.ANTIALIAS`. (was `PIL.Image.NEAREST` prior - to version 2.5.0) + :py:attr:`PIL.Image.ANTIALIAS`. (was :py:attr:`PIL.Image.NEAREST` + prior to version 2.5.0) :returns: None """ From 24cb7bf3df6660bb2bd705b3197e97947dd40a50 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 20 May 2014 17:32:31 +0100 Subject: [PATCH 051/168] Updated changes [ci-skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8121a9519..62d292f72 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Change default interpolation for Image.thumbnail to Image.ANTIALIAS + [hugovk] + +- Add tests and fixes for saving PDFs + [hugovk] + - Remove transparency resource after P->RGBA conversion [hugovk] From 413a086c09a5456ec13729cc4619707280de69bf Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 20 May 2014 10:13:48 -0700 Subject: [PATCH 052/168] Remove READ|WRITE_LIBTIFF guards for LZW/packbits compression tests --- Tests/test_file_libtiff.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index a53593a9e..58fa75239 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -258,9 +258,6 @@ def test_compressions(): im = lena('RGB') out = tempfile('temp.tif') - TiffImagePlugin.READ_LIBTIFF = True - TiffImagePlugin.WRITE_LIBTIFF = True - for compression in ('packbits', 'tiff_lzw'): im.save(out, compression=compression) im2 = Image.open(out) @@ -270,11 +267,6 @@ def test_compressions(): im2 = Image.open(out) assert_image_similar(im, im2, 30) - TiffImagePlugin.READ_LIBTIFF = False - TiffImagePlugin.WRITE_LIBTIFF = False - - - def test_cmyk_save(): im = lena('CMYK') From edd75ceb918be2f299109998bca323886c7af9b9 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 20 May 2014 10:14:41 -0700 Subject: [PATCH 053/168] Use libtiff for writing any compressed tiff files, Fixes #659, #431 --- PIL/TiffImagePlugin.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index fe658d22c..11b92747c 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -984,11 +984,7 @@ def _save(im, fp, filename): compression = im.encoderinfo.get('compression',im.info.get('compression','raw')) - libtiff = WRITE_LIBTIFF or compression in ["tiff_ccitt", "group3", "group4", - "tiff_jpeg", "tiff_adobe_deflate", - "tiff_thunderscan", "tiff_deflate", - "tiff_sgilog", "tiff_sgilog24", - "tiff_raw_16"] + libtiff = WRITE_LIBTIFF or compression != 'raw' # required for color libtiff images ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) From 2662c38f5c5b7b4da8007f51c6020766d9e3f63f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 20 May 2014 12:59:31 -0700 Subject: [PATCH 054/168] Updating docs to reflect current understanding --- docs/reference/ImageDraw.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 68855eb5b..45be43057 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -91,9 +91,12 @@ Methods Draws an arc (a portion of a circle outline) between the start and end angles, inside the given bounding box. - :param xy: Four points to define the bounding box. Sequence of either - ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. - :param outline: Color to use for the outline. + :param xy: Four points to define the bounding box. Sequence of + ``[x0, y0, x1, y1]``. + :param start: Starting angle, in degrees. Angles are measured from + 3 o'clock, increasing clockwise. + :param end: Ending angle, in degrees. + :param fill: Color to use for the arc. .. py:method:: PIL.ImageDraw.Draw.bitmap(xy, bitmap, fill=None) @@ -111,8 +114,8 @@ Methods Same as :py:meth:`~PIL.ImageDraw.Draw.arc`, but connects the end points with a straight line. - :param xy: Four points to define the bounding box. Sequence of either - ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param xy: Four points to define the bounding box. Sequence of + ``[x0, y0, x1, y1]``. :param outline: Color to use for the outline. :param fill: Color to use for the fill. @@ -144,8 +147,8 @@ Methods Same as arc, but also draws straight lines between the end points and the center of the bounding box. - :param xy: Four points to define the bounding box. Sequence of either - ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param xy: Four points to define the bounding box. Sequence of + ``[x0, y0, x1, y1]``. :param outline: Color to use for the outline. :param fill: Color to use for the fill. From 9c048e44d166888b7e6a4e232ed7dafeac67b785 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 21 May 2014 14:30:05 +0300 Subject: [PATCH 055/168] Permission to use and distribute (under the MIT license) this image as a testfile granted by cepreu2github: https://github.com/python-imaging/Pillow/issues/630#issuecomment-43674288 --- Tests/images/junk_jpeg_header.jpg | Bin 0 -> 107466 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Tests/images/junk_jpeg_header.jpg diff --git a/Tests/images/junk_jpeg_header.jpg b/Tests/images/junk_jpeg_header.jpg new file mode 100644 index 0000000000000000000000000000000000000000..564eb3199da25fc050a53940b9c67743896a7e59 GIT binary patch literal 107466 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 JDk3QX|Jm_iy^jC@ literal 0 HcmV?d00001 From aed2646dc21e69d7d7c874778b391f866faf2833 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 21 May 2014 14:32:24 +0300 Subject: [PATCH 056/168] pep8/pyflakes --- Tests/test_file_jpeg.py | 63 +++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 095cad359..b5d43749b 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -12,17 +12,19 @@ if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: test_file = "Images/lena.jpg" + def roundtrip(im, **options): out = BytesIO() im.save(out, "JPEG", **options) bytes = out.tell() out.seek(0) im = Image.open(out) - im.bytes = bytes # for testing only + im.bytes = bytes # for testing only return im # -------------------------------------------------------------------- + def test_sanity(): # internal version number @@ -34,6 +36,7 @@ def test_sanity(): assert_equal(im.size, (128, 128)) assert_equal(im.format, "JPEG") + # -------------------------------------------------------------------- def test_app(): @@ -44,6 +47,7 @@ def test_app(): assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) assert_equal(len(im.applist), 2) + def test_cmyk(): # Test CMYK handling. Thanks to Tim and Charlie for test data, # Michael for getting me to look one more time. @@ -62,6 +66,7 @@ def test_cmyk(): c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] assert_true(k > 0.9) + def test_dpi(): def test(xdpi, ydpi=None): im = Image.open(test_file) @@ -70,7 +75,8 @@ def test_dpi(): assert_equal(test(72), (72, 72)) assert_equal(test(300), (300, 300)) assert_equal(test(100, 200), (100, 200)) - assert_equal(test(0), None) # square pixels + assert_equal(test(0), None) # square pixels + def test_icc(): # Test ICC support @@ -89,6 +95,7 @@ def test_icc(): assert_false(im1.info.get("icc_profile")) assert_true(im2.info.get("icc_profile")) + def test_icc_big(): # Make sure that the "extra" support handles large blocks def test(n): @@ -96,16 +103,20 @@ def test_icc_big(): # using a 4-byte test code should allow us to detect out of # order issues. icc_profile = (b"Test"*int(n/4+1))[:n] - assert len(icc_profile) == n # sanity + assert len(icc_profile) == n # sanity im1 = roundtrip(lena(), icc_profile=icc_profile) assert_equal(im1.info.get("icc_profile"), icc_profile or None) - test(0); test(1) - test(3); test(4); test(5) - test(65533-14) # full JPEG marker block - test(65533-14+1) # full block plus one byte - test(ImageFile.MAXBLOCK) # full buffer block - test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte - test(ImageFile.MAXBLOCK*4+3) # large block + test(0) + test(1) + test(3) + test(4) + test(5) + test(65533-14) # full JPEG marker block + test(65533-14+1) # full block plus one byte + test(ImageFile.MAXBLOCK) # full buffer block + test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte + test(ImageFile.MAXBLOCK*4+3) # large block + def test_optimize(): im1 = roundtrip(lena()) @@ -113,25 +124,29 @@ def test_optimize(): assert_image_equal(im1, im2) assert_true(im1.bytes >= im2.bytes) + def test_optimize_large_buffer(): - #https://github.com/python-imaging/Pillow/issues/148 + # https://github.com/python-imaging/Pillow/issues/148 f = tempfile('temp.jpg') # this requires ~ 1.5x Image.MAXBLOCK - im = Image.new("RGB", (4096,4096), 0xff3333) + im = Image.new("RGB", (4096, 4096), 0xff3333) im.save(f, format="JPEG", optimize=True) + def test_progressive(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), progressive=True) assert_image_equal(im1, im2) assert_true(im1.bytes >= im2.bytes) + def test_progressive_large_buffer(): f = tempfile('temp.jpg') # this requires ~ 1.5x Image.MAXBLOCK - im = Image.new("RGB", (4096,4096), 0xff3333) + im = Image.new("RGB", (4096, 4096), 0xff3333) im.save(f, format="JPEG", progressive=True) + def test_progressive_large_buffer_highest_quality(): f = tempfile('temp.jpg') if py3: @@ -142,16 +157,18 @@ def test_progressive_large_buffer_highest_quality(): # this requires more bytes than pixels in the image im.save(f, format="JPEG", progressive=True, quality=100) + def test_large_exif(): - #https://github.com/python-imaging/Pillow/issues/148 + # https://github.com/python-imaging/Pillow/issues/148 f = tempfile('temp.jpg') im = lena() - im.save(f,'JPEG', quality=90, exif=b"1"*65532) + im.save(f, 'JPEG', quality=90, exif=b"1"*65532) + def test_progressive_compat(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), progressive=1) - im3 = roundtrip(lena(), progression=1) # compatibility + im3 = roundtrip(lena(), progression=1) # compatibility assert_image_equal(im1, im2) assert_image_equal(im1, im3) assert_false(im1.info.get("progressive")) @@ -161,31 +178,34 @@ def test_progressive_compat(): assert_true(im3.info.get("progressive")) assert_true(im3.info.get("progression")) + def test_quality(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), quality=50) assert_image(im1, im2.mode, im2.size) assert_true(im1.bytes >= im2.bytes) + def test_smooth(): im1 = roundtrip(lena()) im2 = roundtrip(lena(), smooth=100) assert_image(im1, im2.mode, im2.size) + def test_subsampling(): def getsampling(im): layer = im.layer return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] # experimental API - im = roundtrip(lena(), subsampling=-1) # default + im = roundtrip(lena(), subsampling=-1) # default assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=0) # 4:4:4 + im = roundtrip(lena(), subsampling=0) # 4:4:4 assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=1) # 4:2:2 + im = roundtrip(lena(), subsampling=1) # 4:2:2 assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=2) # 4:1:1 + im = roundtrip(lena(), subsampling=2) # 4:1:1 assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=3) # default (undefined) + im = roundtrip(lena(), subsampling=3) # default (undefined) assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) im = roundtrip(lena(), subsampling="4:4:4") @@ -197,6 +217,7 @@ def test_subsampling(): assert_exception(TypeError, lambda: roundtrip(lena(), subsampling="1:1:1")) + def test_exif(): im = Image.open("Tests/images/pil_sample_rgb.jpg") info = im._getexif() From 0c1c620e302574e3527cc1d9460acb37536c9614 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 21 May 2014 14:33:28 +0300 Subject: [PATCH 057/168] Add test for #647. Fails without fix. --- Tests/test_file_jpeg.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index b5d43749b..4871c3fbf 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -228,3 +228,11 @@ def test_quality_keep(): im = Image.open("Images/lena.jpg") f = tempfile('temp.jpg') assert_no_exception(lambda: im.save(f, quality='keep')) + + +def test_junk_jpeg_header(): + # https://github.com/python-imaging/Pillow/issues/630 + filename = "Tests/images/junk_jpeg_header.jpg" + assert_no_exception(lambda: Image.open(filename)) + +# End of file From 8a7974c2882cc66cfa28256fbea0d0d67d344b0e Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 21 May 2014 14:35:29 +0300 Subject: [PATCH 058/168] Use bytes for Python 3. It's just an alias to str in Python 2. --- PIL/JpegImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 6f32878e5..7b40d5d4f 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -302,7 +302,7 @@ class JpegImageFile(ImageFile.ImageFile): i = i16(s) else: # Skip non-0xFF junk - s = "\xff" + s = b"\xff" continue if i in MARKER: @@ -320,7 +320,7 @@ class JpegImageFile(ImageFile.ImageFile): s = self.fp.read(1) elif i == 0 or i == 0xFFFF: # padded marker or junk; move on - s = "\xff" + s = b"\xff" else: raise SyntaxError("no marker found") From d594c0241aabeda6725fefc44ccc7f945c0464c9 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 21 May 2014 20:45:02 +0300 Subject: [PATCH 059/168] Rename len variables as length to avoid the built-in function name. --- PIL/PngImagePlugin.py | 78 +++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 2bdf74608..23e77f2fc 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -89,33 +89,33 @@ class ChunkStream: "Fetch a new chunk. Returns header information." if self.queue: - cid, pos, len = self.queue[-1] + cid, pos, length = self.queue[-1] del self.queue[-1] self.fp.seek(pos) else: s = self.fp.read(8) cid = s[4:] pos = self.fp.tell() - len = i32(s) + length = i32(s) if not is_cid(cid): raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) - return cid, pos, len + return cid, pos, length def close(self): self.queue = self.crc = self.fp = None - def push(self, cid, pos, len): + def push(self, cid, pos, length): - self.queue.append((cid, pos, len)) + self.queue.append((cid, pos, length)) - def call(self, cid, pos, len): + def call(self, cid, pos, length): "Call the appropriate chunk handler" if Image.DEBUG: - print("STREAM", cid, pos, len) - return getattr(self, "chunk_" + cid.decode('ascii'))(pos, len) + print("STREAM", cid, pos, length) + return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length) def crc(self, cid, data): "Read and verify checksum" @@ -139,10 +139,10 @@ class ChunkStream: cids = [] while True: - cid, pos, len = self.read() + cid, pos, length = self.read() if cid == endchunk: break - self.crc(cid, ImageFile._safe_read(self.fp, len)) + self.crc(cid, ImageFile._safe_read(self.fp, length)) cids.append(cid) return cids @@ -190,10 +190,10 @@ class PngStream(ChunkStream): self.im_tile = None self.im_palette = None - def chunk_iCCP(self, pos, len): + def chunk_iCCP(self, pos, length): # ICC profile - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) # Null separator 1 byte (null character) @@ -213,10 +213,10 @@ class PngStream(ChunkStream): self.im_info["icc_profile"] = icc_profile return s - def chunk_IHDR(self, pos, len): + def chunk_IHDR(self, pos, length): # image header - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) self.im_size = i32(s), i32(s[4:]) try: self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))] @@ -228,30 +228,30 @@ class PngStream(ChunkStream): raise SyntaxError("unknown filter category") return s - def chunk_IDAT(self, pos, len): + def chunk_IDAT(self, pos, length): # image data self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)] - self.im_idat = len + self.im_idat = length raise EOFError - def chunk_IEND(self, pos, len): + def chunk_IEND(self, pos, length): # end of PNG image raise EOFError - def chunk_PLTE(self, pos, len): + def chunk_PLTE(self, pos, length): # palette - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": self.im_palette = "RGB", s return s - def chunk_tRNS(self, pos, len): + def chunk_tRNS(self, pos, length): # transparency - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": if _simple_palette.match(s): i = s.find(b"\0") @@ -265,17 +265,17 @@ class PngStream(ChunkStream): self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) return s - def chunk_gAMA(self, pos, len): + def chunk_gAMA(self, pos, length): # gamma setting - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) self.im_info["gamma"] = i32(s) / 100000.0 return s - def chunk_pHYs(self, pos, len): + def chunk_pHYs(self, pos, length): # pixels per unit - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) px, py = i32(s), i32(s[4:]) unit = i8(s[8]) if unit == 1: # meter @@ -285,10 +285,10 @@ class PngStream(ChunkStream): self.im_info["aspect"] = px, py return s - def chunk_tEXt(self, pos, len): + def chunk_tEXt(self, pos, length): # text - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) try: k, v = s.split(b"\0", 1) except ValueError: @@ -301,10 +301,10 @@ class PngStream(ChunkStream): self.im_info[k] = self.im_text[k] = v return s - def chunk_zTXt(self, pos, len): + def chunk_zTXt(self, pos, length): # compressed text - s = ImageFile._safe_read(self.fp, len) + s = ImageFile._safe_read(self.fp, length) try: k, v = s.split(b"\0", 1) except ValueError: @@ -358,16 +358,16 @@ class PngImageFile(ImageFile.ImageFile): # # get next chunk - cid, pos, len = self.png.read() + cid, pos, length = self.png.read() try: - s = self.png.call(cid, pos, len) + s = self.png.call(cid, pos, length) except EOFError: break except AttributeError: if Image.DEBUG: - print(cid, pos, len, "(unknown)") - s = ImageFile._safe_read(self.fp, len) + print(cid, pos, length, "(unknown)") + s = ImageFile._safe_read(self.fp, length) self.png.crc(cid, s) @@ -388,7 +388,7 @@ class PngImageFile(ImageFile.ImageFile): rawmode, data = self.png.im_palette self.palette = ImagePalette.raw(rawmode, data) - self.__idat = len # used by load_read() + self.__idat = length # used by load_read() def verify(self): @@ -421,13 +421,13 @@ class PngImageFile(ImageFile.ImageFile): self.fp.read(4) # CRC - cid, pos, len = self.png.read() + cid, pos, length = self.png.read() if cid not in [b"IDAT", b"DDAT"]: - self.png.push(cid, pos, len) + self.png.push(cid, pos, length) return b"" - self.__idat = len # empty chunks are allowed + self.__idat = length # empty chunks are allowed # read more data from this chunk if bytes <= 0: @@ -560,7 +560,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): chunk(fp, b"PLTE", palette_bytes) transparency = im.encoderinfo.get('transparency',im.info.get('transparency', None)) - + if transparency or transparency == 0: if im.mode == "P": # limit to actual palette size @@ -580,7 +580,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): else: if "transparency" in im.encoderinfo: # don't bother with transparency if it's an RGBA - # and it's in the info dict. It's probably just stale. + # and it's in the info dict. It's probably just stale. raise IOError("cannot use transparency for this mode") else: if im.mode == "P" and im.im.getpalettemode() == "RGBA": From 14c2f86873777f0a74ab5217bac75f5bbd10409b Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 21 May 2014 21:33:49 +0300 Subject: [PATCH 060/168] Rename bytes variable as read_bytes to avoid the built-in function name. --- PIL/PngImagePlugin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 23e77f2fc..e794ef702 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -413,7 +413,7 @@ class PngImageFile(ImageFile.ImageFile): ImageFile.ImageFile.load_prepare(self) - def load_read(self, bytes): + def load_read(self, read_bytes): "internal: read more image data" while self.__idat == 0: @@ -430,14 +430,14 @@ class PngImageFile(ImageFile.ImageFile): self.__idat = length # empty chunks are allowed # read more data from this chunk - if bytes <= 0: - bytes = self.__idat + if read_bytes <= 0: + read_bytes = self.__idat else: - bytes = min(bytes, self.__idat) + read_bytes = min(read_bytes, self.__idat) - self.__idat = self.__idat - bytes + self.__idat = self.__idat - read_bytes - return self.fp.read(bytes) + return self.fp.read(read_bytes) def load_end(self): From de28007585eca582a1648ed59af93fffe611ef88 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 23 May 2014 00:07:58 +0100 Subject: [PATCH 061/168] Update CHANGES.rst [ci-skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 62d292f72..5c268b804 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Ignore junk JPEG markers + [hugovk] + - Change default interpolation for Image.thumbnail to Image.ANTIALIAS [hugovk] From b3dcf849b8b9b828510f0e2236dbeb04487d4d3f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 24 May 2014 12:46:55 +0100 Subject: [PATCH 062/168] Update --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5c268b804..3964b798d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Rename variables not to use built-in function names + [hugovk] + - Ignore junk JPEG markers [hugovk] From dee8af10acb62c6a11e8a2a22886faf69ce1204a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 24 May 2014 13:11:46 +0100 Subject: [PATCH 063/168] Update CHANGES.rst [ci-skip] --- CHANGES.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3964b798d..8d86333df 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,7 +3,12 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ - +- Fixed resolution handling for EPS thumbnails + [eliempje] + +- Fixed rendering of some binary EPS files (Issue #302) + [eliempje] + - Rename variables not to use built-in function names [hugovk] From c2bd74b397e742ab5b0ff724e0f168d23bcb1f39 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 24 May 2014 13:15:23 +0100 Subject: [PATCH 064/168] Update CHANGES.rst [ci-skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8d86333df..518c5550f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Support for pickling Image objects + [hugovk] + - Fixed resolution handling for EPS thumbnails [eliempje] From 8a8aec534b968e9645f1daf15fb9ea00048ef32e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sat, 24 May 2014 13:17:43 +0100 Subject: [PATCH 065/168] Update CHANGES.rst [ci-skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 518c5550f..b19ef359d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Use libtiff to write any compressed tiff files + [wiredfool] + - Support for pickling Image objects [hugovk] From 35336e5afd5e3eb9ea2991eaf3b705877c49a173 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 24 May 2014 17:31:37 +0300 Subject: [PATCH 066/168] Test arc, chord, pieslice give TypeError when bounding box is [(x0, y0), (x1, y1)] --- Tests/test_imagedraw.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 0a9366928..21eca5038 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -62,9 +62,8 @@ def helper_arc(bbox): assert_image_equal(im, Image.open("Tests/images/imagedraw_arc.png")) -# FIXME -# def test_arc1(): -# helper_arc(bbox1) +def test_arc1(): + assert_exception(TypeError, lambda: helper_arc(bbox1)) def test_arc2(): @@ -98,9 +97,8 @@ def helper_chord(bbox): assert_image_equal(im, Image.open("Tests/images/imagedraw_chord.png")) -# FIXME -# def test_chord1(): -# helper_chord(bbox1) +def test_chord1(): + assert_exception(TypeError, lambda: helper_chord(bbox1)) def test_chord2(): @@ -162,9 +160,8 @@ def helper_pieslice(bbox): assert_image_equal(im, Image.open("Tests/images/imagedraw_pieslice.png")) -# FIXME -# def test_pieslice1(): -# helper_pieslice(bbox1) +def test_pieslice1(): + assert_exception(TypeError, lambda: helper_pieslice(bbox1)) def test_pieslice2(): From 8f92562ec353e7a2efeb96fb85aa5d76f3270492 Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 24 May 2014 17:36:31 +0300 Subject: [PATCH 067/168] Remove old FIXME comment [CI skip] --- Tests/test_imagedraw.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 21eca5038..2e0cc07c0 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -53,7 +53,6 @@ def helper_arc(bbox): draw = ImageDraw.Draw(im) # Act - # FIXME Should docs note 0 degrees is at 3 o'clock? # FIXME Fill param should be named outline. draw.arc(bbox, 0, 180) del draw From e6908401d4935ebe368c15088e56f0bbf9687634 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 26 May 2014 19:19:27 +0300 Subject: [PATCH 068/168] Add config file for coverage.py to exclude some common things: http://nedbatchelder.com/code/coverage/config.html --- .coveragerc | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..bd93c4749 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,11 @@ +# .coveragerc to control coverage.py + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma: + pragma: no cover + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: From 268c419d7c2beeb2fdbfdbdee27b7f4026778522 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 26 May 2014 20:09:12 +0300 Subject: [PATCH 069/168] Simple spider test. Test image created with Pillow. --- Tests/images/lena.spider | Bin 0 -> 66560 bytes Tests/test_file_spider.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 Tests/images/lena.spider create mode 100644 Tests/test_file_spider.py diff --git a/Tests/images/lena.spider b/Tests/images/lena.spider new file mode 100644 index 0000000000000000000000000000000000000000..df963c1c3cfa6b95f7337611c7b932edaa57b4a4 GIT binary patch literal 66560 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 Oh5uaG?`2CbM*kmXvmjIe literal 0 HcmV?d00001 diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py new file mode 100644 index 000000000..9c15de804 --- /dev/null +++ b/Tests/test_file_spider.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +test_file = "Tests/images/lena.spider" + + +def test_sanity(): + im = Image.open(test_file) + im.load() + assert_equal(im.mode, "F") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "SPIDER") + +# End of file From 5f9cad1a8eea23254889788222c92cf20a28d829 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 26 May 2014 20:15:48 +0300 Subject: [PATCH 070/168] pyflakes --- PIL/SpiderImagePlugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index cd97c524d..ec8267f22 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -74,11 +74,9 @@ def isSpiderImage(filename): fp = open(filename,'rb') f = fp.read(92) # read 23 * 4 bytes fp.close() - bigendian = 1 t = struct.unpack('>23f',f) # try big-endian first hdrlen = isSpiderHeader(t) if hdrlen == 0: - bigendian = 0 t = struct.unpack('<23f',f) # little-endian hdrlen = isSpiderHeader(t) return hdrlen From 14c8d8afe4d01275d2d8973e53c7ab4e32fd3d31 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 26 May 2014 21:00:20 +0300 Subject: [PATCH 071/168] More spider tests --- Tests/test_file_spider.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 9c15de804..d93724492 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -1,6 +1,7 @@ from tester import * from PIL import Image +from PIL import SpiderImagePlugin test_file = "Tests/images/lena.spider" @@ -12,4 +13,24 @@ def test_sanity(): assert_equal(im.size, (128, 128)) assert_equal(im.format, "SPIDER") + +def test_save(): + # Arrange + temp = tempfile('temp.spider') + im = lena() + + # Act + im.save(temp, "SPIDER") + + # Assert + im2 = Image.open(temp) + assert_equal(im2.mode, "F") + assert_equal(im2.size, (128, 128)) + assert_equal(im2.format, "SPIDER") + + +def test_isSpiderImage(): + assert_true(SpiderImagePlugin.isSpiderImage(test_file)) + + # End of file From 5217a523d677067cf3d268b1941f0fb47ca75de3 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 26 May 2014 21:18:48 +0300 Subject: [PATCH 072/168] Make sure nvalues is int and not float for Python 3 --- PIL/SpiderImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index ec8267f22..0810b929e 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -208,7 +208,7 @@ def makeSpiderHeader(im): if 1024%lenbyt != 0: labrec += 1 labbyt = labrec * lenbyt hdr = [] - nvalues = labbyt / 4 + nvalues = int(labbyt / 4) for i in range(nvalues): hdr.append(0.0) From eacbd7b04abac95a2f84a3f091642530910f450f Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 26 May 2014 21:56:40 +0300 Subject: [PATCH 073/168] pep8/pyflakes --- PIL/SpiderImagePlugin.py | 80 ++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index 0810b929e..306b348bc 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -36,17 +36,23 @@ from __future__ import print_function from PIL import Image, ImageFile -import os, struct, sys +import os +import struct +import sys + def isInt(f): try: i = int(f) - if f-i == 0: return 1 - else: return 0 + if f-i == 0: + return 1 + else: + return 0 except: return 0 -iforms = [1,3,-11,-12,-21,-22] +iforms = [1, 3, -11, -12, -21, -22] + # There is no magic number to identify Spider files, so just check a # series of header locations to see if they have reasonable values. @@ -56,28 +62,32 @@ iforms = [1,3,-11,-12,-21,-22] def isSpiderHeader(t): h = (99,) + t # add 1 value so can use spider header index start=1 # header values 1,2,5,12,13,22,23 should be integers - for i in [1,2,5,12,13,22,23]: - if not isInt(h[i]): return 0 + for i in [1, 2, 5, 12, 13, 22, 23]: + if not isInt(h[i]): + return 0 # check iform iform = int(h[5]) - if not iform in iforms: return 0 + if iform not in iforms: + return 0 # check other header values labrec = int(h[13]) # no. records in file header labbyt = int(h[22]) # total no. of bytes in header lenbyt = int(h[23]) # record length in bytes - #print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt) - if labbyt != (labrec * lenbyt): return 0 + # print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt) + if labbyt != (labrec * lenbyt): + return 0 # looks like a valid header return labbyt + def isSpiderImage(filename): - fp = open(filename,'rb') + fp = open(filename, 'rb') f = fp.read(92) # read 23 * 4 bytes fp.close() - t = struct.unpack('>23f',f) # try big-endian first + t = struct.unpack('>23f', f) # try big-endian first hdrlen = isSpiderHeader(t) if hdrlen == 0: - t = struct.unpack('<23f',f) # little-endian + t = struct.unpack('<23f', f) # little-endian hdrlen = isSpiderHeader(t) return hdrlen @@ -94,11 +104,11 @@ class SpiderImageFile(ImageFile.ImageFile): try: self.bigendian = 1 - t = struct.unpack('>27f',f) # try big-endian first + t = struct.unpack('>27f', f) # try big-endian first hdrlen = isSpiderHeader(t) if hdrlen == 0: self.bigendian = 0 - t = struct.unpack('<27f',f) # little-endian + t = struct.unpack('<27f', f) # little-endian hdrlen = isSpiderHeader(t) if hdrlen == 0: raise SyntaxError("not a valid Spider file") @@ -110,7 +120,7 @@ class SpiderImageFile(ImageFile.ImageFile): if iform != 1: raise SyntaxError("not a Spider 2D image") - self.size = int(h[12]), int(h[2]) # size in pixels (width, height) + self.size = int(h[12]), int(h[2]) # size in pixels (width, height) self.istack = int(h[24]) self.imgnumber = int(h[27]) @@ -139,9 +149,10 @@ class SpiderImageFile(ImageFile.ImageFile): self.rawmode = "F;32F" self.mode = "F" - self.tile = [("raw", (0, 0) + self.size, offset, - (self.rawmode, 0, 1))] - self.__fp = self.fp # FIXME: hack + self.tile = [ + ("raw", (0, 0) + self.size, offset, + (self.rawmode, 0, 1))] + self.__fp = self.fp # FIXME: hack # 1st image index is zero (although SPIDER imgnumber starts at 1) def tell(self): @@ -174,6 +185,7 @@ class SpiderImageFile(ImageFile.ImageFile): from PIL import ImageTk return ImageTk.PhotoImage(self.convert2byte(), palette=256) + # -------------------------------------------------------------------- # Image series @@ -198,14 +210,16 @@ def loadImageSeries(filelist=None): imglist.append(im) return imglist + # -------------------------------------------------------------------- # For saving images in Spider format def makeSpiderHeader(im): - nsam,nrow = im.size + nsam, nrow = im.size lenbyt = nsam * 4 # There are labrec records in the header labrec = 1024 / lenbyt - if 1024%lenbyt != 0: labrec += 1 + if 1024 % lenbyt != 0: + labrec += 1 labbyt = labrec * lenbyt hdr = [] nvalues = int(labbyt / 4) @@ -216,13 +230,13 @@ def makeSpiderHeader(im): return [] # NB these are Fortran indices - hdr[1] = 1.0 # nslice (=1 for an image) - hdr[2] = float(nrow) # number of rows per slice - hdr[5] = 1.0 # iform for 2D image - hdr[12] = float(nsam) # number of pixels per line - hdr[13] = float(labrec) # number of records in file header - hdr[22] = float(labbyt) # total number of bytes in header - hdr[23] = float(lenbyt) # record length in bytes + hdr[1] = 1.0 # nslice (=1 for an image) + hdr[2] = float(nrow) # number of rows per slice + hdr[5] = 1.0 # iform for 2D image + hdr[12] = float(nsam) # number of pixels per line + hdr[13] = float(labrec) # number of records in file header + hdr[22] = float(labbyt) # total number of bytes in header + hdr[23] = float(lenbyt) # record length in bytes # adjust for Fortran indexing hdr = hdr[1:] @@ -230,9 +244,10 @@ def makeSpiderHeader(im): # pack binary data into a string hdrstr = [] for v in hdr: - hdrstr.append(struct.pack('f',v)) + hdrstr.append(struct.pack('f', v)) return hdrstr + def _save(im, fp, filename): if im.mode[0] != "F": im = im.convert('F') @@ -248,11 +263,12 @@ def _save(im, fp, filename): raise IOError("Unable to open %s for writing" % filename) fp.writelines(hdr) - rawmode = "F;32NF" #32-bit native floating point - ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode,0,1))]) + rawmode = "F;32NF" # 32-bit native floating point + ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))]) fp.close() + def _save_spider(im, fp, filename): # get the filename extension and register it with Image fn, ext = os.path.splitext(filename) @@ -290,5 +306,7 @@ if __name__ == "__main__": if outfile != "": # perform some image operation im = im.transpose(Image.FLIP_LEFT_RIGHT) - print("saving a flipped version of %s as %s " % (os.path.basename(filename), outfile)) + print( + "saving a flipped version of %s as %s " % + (os.path.basename(filename), outfile)) im.save(outfile, "SPIDER") From 1011e51083e475ddea26e6e15f4772a22d505ddb Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 27 May 2014 12:43:54 +0100 Subject: [PATCH 074/168] Added support for OpenJPEG 2.1. --- PIL/Jpeg2KImagePlugin.py | 16 ++++++++--- decode.c | 6 +++-- libImaging/Jpeg2K.h | 5 +++- libImaging/Jpeg2KDecode.c | 13 +++++++++ libImaging/Jpeg2KEncode.c | 4 +++ setup.py | 56 ++++++++++++++++++++++++++++++--------- 6 files changed, 81 insertions(+), 19 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index f57f4a784..31e6f0a13 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -155,15 +155,25 @@ class Jpeg2KImageFile(ImageFile.ImageFile): self.layers = 0 fd = -1 + length = -1 if hasattr(self.fp, "fileno"): try: fd = self.fp.fileno() + length = os.fstat(fd).st_size except: fd = -1 - + elif hasattr(self.fp, "seek"): + try: + pos = f.tell() + seek(0, 2) + length = f.tell() + seek(pos, 0) + except: + length = -1 + self.tile = [('jpeg2k', (0, 0) + self.size, 0, - (self.codec, self.reduce, self.layers, fd))] + (self.codec, self.reduce, self.layers, fd, length))] def load(self): if self.reduce: @@ -175,7 +185,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile): if self.tile: # Update the reduce and layers settings t = self.tile[0] - t3 = (t[3][0], self.reduce, self.layers, t[3][3]) + t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4]) self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] ImageFile.ImageFile.load(self) diff --git a/decode.c b/decode.c index 33367dfe3..d5e329384 100644 --- a/decode.c +++ b/decode.c @@ -797,8 +797,9 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) int reduce = 0; int layers = 0; int fd = -1; - if (!PyArg_ParseTuple(args, "ss|iii", &mode, &format, - &reduce, &layers, &fd)) + PY_LONG_LONG length = -1; + if (!PyArg_ParseTuple(args, "ss|iiiL", &mode, &format, + &reduce, &layers, &fd, &length)) return NULL; if (strcmp(format, "j2k") == 0) @@ -821,6 +822,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) context = (JPEG2KDECODESTATE *)decoder->state.context; context->fd = fd; + context->length = (off_t)length; context->format = codec_format; context->reduce = reduce; context->layers = layers; diff --git a/libImaging/Jpeg2K.h b/libImaging/Jpeg2K.h index be6e0770b..fd53b59df 100644 --- a/libImaging/Jpeg2K.h +++ b/libImaging/Jpeg2K.h @@ -8,7 +8,7 @@ * Copyright (c) 2014 by Alastair Houghton */ -#include +#include /* -------------------------------------------------------------------- */ /* Decoder */ @@ -20,6 +20,9 @@ typedef struct { /* File descriptor, if available; otherwise, -1 */ int fd; + /* Length of data, if available; otherwise, -1 */ + off_t length; + /* Specify the desired format */ OPJ_CODEC_FORMAT format; diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 6b6176c78..43a796c27 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -517,7 +517,20 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, opj_stream_set_read_function(stream, j2k_read); opj_stream_set_skip_function(stream, j2k_skip); +#if OPJ_VERSION_MAJOR == 2 && OPJ_VERSION_MINOR == 0 opj_stream_set_user_data(stream, decoder); +#else + opj_stream_set_user_data(stream, decoder, NULL); + + /* Hack: if we don't know the length, the largest file we can + possibly support is 4GB. We can't go larger than this, because + OpenJPEG truncates this value for the final box in the file, and + the box lengths in OpenJPEG are currently 32 bit. */ + if (context->length < 0) + opj_stream_set_user_data_length(stream, 0xffffffff); + else + opj_stream_set_user_data_length(stream, context->length); +#endif /* Setup decompression context */ context->error_msg = NULL; diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index c1e16e97f..f1748b97e 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -259,7 +259,11 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, opj_stream_set_skip_function(stream, j2k_skip); opj_stream_set_seek_function(stream, j2k_seek); +#if OPJ_VERSION_MAJOR == 2 && OPJ_VERSION_MINOR == 0 opj_stream_set_user_data(stream, encoder); +#else + opj_stream_set_user_data(stream, encoder, NULL); +#endif /* Setup an opj_image */ if (strcmp (im->mode, "L") == 0) { diff --git a/setup.py b/setup.py index 50ca985e3..a7dd94190 100644 --- a/setup.py +++ b/setup.py @@ -337,14 +337,23 @@ class pil_build_ext(build_ext): _add_directory(include_dirs, "/usr/include") # on Windows, look for the OpenJPEG libraries in the location that - # the official installed puts them + # the official installer puts them if sys.platform == "win32": - _add_directory(library_dirs, - os.path.join(os.environ.get("ProgramFiles", ""), - "OpenJPEG 2.0", "lib")) - _add_directory(include_dirs, - os.path.join(os.environ.get("ProgramFiles", ""), - "OpenJPEG 2.0", "include")) + program_files = os.environ.get('ProgramFiles', '') + best_version = (0, 0) + best_path = None + for name in os.listdir(program_files): + if name.startswith('OpenJPEG '): + version = tuple([int(x) for x in name[9:].strip().split('.')]) + if version > best_version: + best_version = version + best_path = os.path.join(program_files, name) + + if best_path: + _add_directory(library_dirs, + os.path.join(best_path, 'lib')) + _add_directory(include_dirs, + os.path.join(best_path, 'include')) # # insert new dirs *before* default libs, to avoid conflicts @@ -375,10 +384,28 @@ class pil_build_ext(build_ext): feature.jpeg = "libjpeg" # alternative name if feature.want('jpeg2000'): - if _find_include_file(self, "openjpeg-2.0/openjpeg.h"): - if _find_library_file(self, "openjp2"): - feature.jpeg2000 = "openjp2" - + best_version = None + best_path = None + + # Find the best version + for directory in self.compiler.include_dirs: + for name in os.listdir(directory): + if name.startswith('openjpeg-') and \ + os.path.isfile(os.path.join(directory, name, + 'openjpeg.h')): + version = tuple([int(x) for x in name[9:].split('.')]) + if best_version is None or version > best_version: + best_version = version + best_path = os.path.join(directory, name) + + if best_version and _find_library_file(self, 'openjp2'): + # Add the directory to the include path so we can include + # rather than having to cope with the versioned + # include path + _add_directory(self.compiler.include_dirs, best_path, 0) + feature.jpeg2000 = 'openjp2' + feature.openjpeg_version = '.'.join([str(x) for x in best_version]) + if feature.want('tiff'): if _find_library_file(self, "tiff"): feature.tiff = "tiff" @@ -572,7 +599,7 @@ class pil_build_ext(build_ext): options = [ (feature.tcl and feature.tk, "TKINTER"), (feature.jpeg, "JPEG"), - (feature.jpeg2000, "OPENJPEG (JPEG2000)"), + (feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version), (feature.zlib, "ZLIB (PNG/ZIP)"), (feature.tiff, "LIBTIFF"), (feature.freetype, "FREETYPE2"), @@ -583,7 +610,10 @@ class pil_build_ext(build_ext): all = 1 for option in options: if option[0]: - print("--- %s support available" % option[1]) + version = '' + if len(option) >= 3: + version = ' (%s)' % option[2] + print("--- %s support available%s" % (option[1], version)) else: print("*** %s support not available" % option[1]) if option[1] == "TKINTER" and _tkinter: From 72ecb8752e283c199f14ed29f15336d0a42cdd41 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 27 May 2014 12:52:50 +0100 Subject: [PATCH 075/168] Changed test for OpenJPEG 2.0 --- libImaging/Jpeg2KDecode.c | 3 ++- libImaging/Jpeg2KEncode.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 43a796c27..1b61b4f7d 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -517,7 +517,8 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, opj_stream_set_read_function(stream, j2k_read); opj_stream_set_skip_function(stream, j2k_skip); -#if OPJ_VERSION_MAJOR == 2 && OPJ_VERSION_MINOR == 0 + /* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */ +#ifndef OPJ_VERSION_MAJOR opj_stream_set_user_data(stream, decoder); #else opj_stream_set_user_data(stream, decoder, NULL); diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index f1748b97e..8e7d0d1f2 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -259,7 +259,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, opj_stream_set_skip_function(stream, j2k_skip); opj_stream_set_seek_function(stream, j2k_seek); -#if OPJ_VERSION_MAJOR == 2 && OPJ_VERSION_MINOR == 0 + /* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */ +#ifndef OPJ_VERSION_MAJOR opj_stream_set_user_data(stream, encoder); #else opj_stream_set_user_data(stream, encoder, NULL); From 87d10dcaebec1ce9b3c2217c592c07da76e54252 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 27 May 2014 15:05:25 +0100 Subject: [PATCH 076/168] Oops. Fixed a silly mistake. --- PIL/Jpeg2KImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 31e6f0a13..6b5fbdedc 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -166,9 +166,9 @@ class Jpeg2KImageFile(ImageFile.ImageFile): elif hasattr(self.fp, "seek"): try: pos = f.tell() - seek(0, 2) + f.seek(0, 2) length = f.tell() - seek(pos, 0) + f.seek(pos, 0) except: length = -1 From ae37743ac743bc4f23cc7caa787a541fdb3e9651 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 27 May 2014 16:58:26 +0300 Subject: [PATCH 077/168] Both kinds of bounding box for arc, chord and pieslice. --- Tests/test_imagedraw.py | 6 +-- _imaging.c | 109 ++++++++++++++++++++++++++++++---------- 2 files changed, 86 insertions(+), 29 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 2e0cc07c0..c47638a05 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -62,7 +62,7 @@ def helper_arc(bbox): def test_arc1(): - assert_exception(TypeError, lambda: helper_arc(bbox1)) + helper_arc(bbox1) def test_arc2(): @@ -97,7 +97,7 @@ def helper_chord(bbox): def test_chord1(): - assert_exception(TypeError, lambda: helper_chord(bbox1)) + helper_chord(bbox1) def test_chord2(): @@ -160,7 +160,7 @@ def helper_pieslice(bbox): def test_pieslice1(): - assert_exception(TypeError, lambda: helper_pieslice(bbox1)) + helper_pieslice(bbox1) def test_pieslice2(): diff --git a/_imaging.c b/_imaging.c index c47868b81..f8fd96f8a 100644 --- a/_imaging.c +++ b/_imaging.c @@ -2252,17 +2252,17 @@ void _font_text_asBytes(PyObject* encoded_string, unsigned char** text){ if (bytes) { *text = (unsigned char*)PyBytes_AsString(bytes); return; - } + } #if PY_VERSION_HEX < 0x03000000 /* likely case here is py2.x with an ordinary string. but this isn't defined in Py3.x */ if (PyString_Check(encoded_string)) { *text = (unsigned char *)PyString_AsString(encoded_string); - } + } #endif } - + static PyObject* _font_getmask(ImagingFontObject* self, PyObject* args) @@ -2336,7 +2336,7 @@ _font_getsize(ImagingFontObject* self, PyObject* args) return NULL; } - return Py_BuildValue("ii", textwidth(self, text), self->ysize); + return Py_BuildValue("ii", textwidth(self, text), self->ysize); } static struct PyMethodDef _font_methods[] = { @@ -2399,17 +2399,35 @@ _draw_ink(ImagingDrawObject* self, PyObject* args) static PyObject* _draw_arc(ImagingDrawObject* self, PyObject* args) { - int x0, y0, x1, y1; + double* xy; + int n; + + PyObject* data; int ink; int start, end; int op = 0; - if (!PyArg_ParseTuple(args, "(iiii)iii|i", - &x0, &y0, &x1, &y1, - &start, &end, &ink)) + if (!PyArg_ParseTuple(args, "Oiii|i", &data, &start, &end, &ink)) return NULL; - if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end, - &ink, op) < 0) + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + if (n != 2) { + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; + } + + n = ImagingDrawArc(self->image->image, + (int) xy[0], (int) xy[1], + (int) xy[2], (int) xy[3], + start, end, &ink, op + ); + + free(xy); + + if (n < 0) return NULL; Py_INCREF(Py_None); @@ -2455,15 +2473,35 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args) static PyObject* _draw_chord(ImagingDrawObject* self, PyObject* args) { - int x0, y0, x1, y1; + double* xy; + int n; + + PyObject* data; int ink, fill; int start, end; - if (!PyArg_ParseTuple(args, "(iiii)iiii", - &x0, &y0, &x1, &y1, &start, &end, &ink, &fill)) + if (!PyArg_ParseTuple(args, "Oiiii", + &data, &start, &end, &ink, &fill)) return NULL; - if (ImagingDrawChord(self->image->image, x0, y0, x1, y1, - start, end, &ink, fill, self->blend) < 0) + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + if (n != 2) { + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; + } + + n = ImagingDrawChord(self->image->image, + (int) xy[0], (int) xy[1], + (int) xy[2], (int) xy[3], + start, end, &ink, fill, self->blend + ); + + free(xy); + + if (n < 0) return NULL; Py_INCREF(Py_None); @@ -2492,8 +2530,8 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args) return NULL; } - n = ImagingDrawEllipse(self->image->image, - (int) xy[0], (int) xy[1], + n = ImagingDrawEllipse(self->image->image, + (int) xy[0], (int) xy[1], (int) xy[2], (int) xy[3], &ink, fill, self->blend ); @@ -2656,15 +2694,34 @@ _draw_outline(ImagingDrawObject* self, PyObject* args) static PyObject* _draw_pieslice(ImagingDrawObject* self, PyObject* args) { - int x0, y0, x1, y1; + double* xy; + int n; + + PyObject* data; int ink, fill; int start, end; - if (!PyArg_ParseTuple(args, "(iiii)iiii", - &x0, &y0, &x1, &y1, &start, &end, &ink, &fill)) + if (!PyArg_ParseTuple(args, "Oiiii", &data, &start, &end, &ink, &fill)) return NULL; - if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1, - start, end, &ink, fill, self->blend) < 0) + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + if (n != 2) { + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; + } + + n = ImagingDrawPieslice(self->image->image, + (int) xy[0], (int) xy[1], + (int) xy[2], (int) xy[3], + start, end, &ink, fill, self->blend + ); + + free(xy); + + if (n < 0) return NULL; Py_INCREF(Py_None); @@ -2738,9 +2795,9 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args) return NULL; } - n = ImagingDrawRectangle(self->image->image, + n = ImagingDrawRectangle(self->image->image, (int) xy[0], (int) xy[1], - (int) xy[2], (int) xy[3], + (int) xy[2], (int) xy[3], &ink, fill, self->blend ); @@ -3117,8 +3174,8 @@ _getattr_ptr(ImagingObject* self, void* closure) static PyObject* _getattr_unsafe_ptrs(ImagingObject* self, void* closure) { - return Py_BuildValue("(ss)(si)(si)(si)(si)(si)(sn)(sn)(sn)(sn)(sn)(si)(si)(sn)", - "mode", self->image->mode, + return Py_BuildValue("(ss)(si)(si)(si)(si)(si)(sn)(sn)(sn)(sn)(sn)(si)(si)(sn)", + "mode", self->image->mode, "type", self->image->type, "depth", self->image->depth, "bands", self->image->bands, From 4dcae4402cca5ed75e33c038778c291691095a7d Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 27 May 2014 23:55:43 +0300 Subject: [PATCH 078/168] Add back the other bounding box [CI skip] --- docs/reference/ImageDraw.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 45be43057..30aa15a9b 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -91,11 +91,11 @@ Methods Draws an arc (a portion of a circle outline) between the start and end angles, inside the given bounding box. - :param xy: Four points to define the bounding box. Sequence of - ``[x0, y0, x1, y1]``. - :param start: Starting angle, in degrees. Angles are measured from + :param xy: Four points to define the bounding box. Sequence of + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param start: Starting angle, in degrees. Angles are measured from 3 o'clock, increasing clockwise. - :param end: Ending angle, in degrees. + :param end: Ending angle, in degrees. :param fill: Color to use for the arc. .. py:method:: PIL.ImageDraw.Draw.bitmap(xy, bitmap, fill=None) @@ -114,8 +114,8 @@ Methods Same as :py:meth:`~PIL.ImageDraw.Draw.arc`, but connects the end points with a straight line. - :param xy: Four points to define the bounding box. Sequence of - ``[x0, y0, x1, y1]``. + :param xy: Four points to define the bounding box. Sequence of + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. :param outline: Color to use for the outline. :param fill: Color to use for the fill. @@ -148,7 +148,7 @@ Methods center of the bounding box. :param xy: Four points to define the bounding box. Sequence of - ``[x0, y0, x1, y1]``. + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. :param outline: Color to use for the outline. :param fill: Color to use for the fill. From 085527e01db9ae6cbbefbd278ab0ebfd8d94c998 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 30 May 2014 08:47:26 -0700 Subject: [PATCH 079/168] Updated Changes.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b19ef359d..a8dd7d137 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Added tests for Spider files + [hugovk] + - Use libtiff to write any compressed tiff files [wiredfool] From f4ddf1be9775c4a034704ba19c53e7e9ea4703ab Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 30 May 2014 15:08:21 -0700 Subject: [PATCH 080/168] Initialize openjpeg_version --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index a7dd94190..d385ba8a2 100644 --- a/setup.py +++ b/setup.py @@ -101,7 +101,7 @@ class pil_build_ext(build_ext): class feature: zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None - jpeg2000 = None + jpeg2000 = openjpeg_version = None required = [] def require(self, feat): @@ -611,7 +611,7 @@ class pil_build_ext(build_ext): for option in options: if option[0]: version = '' - if len(option) >= 3: + if len(option) >= 3 and option[2]: version = ' (%s)' % option[2] print("--- %s support available%s" % (option[1], version)) else: From 6dda7601afed8d0f26c8535704001b358c1b24ce Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 30 May 2014 15:37:35 -0700 Subject: [PATCH 081/168] Testing against newer version of OpenJPEG --- depends/install_openjpeg.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/depends/install_openjpeg.sh b/depends/install_openjpeg.sh index bd6b83e3b..b3a6ccc4d 100755 --- a/depends/install_openjpeg.sh +++ b/depends/install_openjpeg.sh @@ -2,15 +2,16 @@ # install openjpeg -if [ ! -f openjpeg-2.0.0.tar.gz ]; then - wget 'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz' +if [ ! -f openjpeg-2.1.0.tar.gz ]; then + wget 'http://iweb.dl.sourceforge.net/project/openjpeg.mirror/2.1.0/openjpeg-2.1.0.tar.gz' + fi -rm -r openjpeg-2.0.0 -tar -xvzf openjpeg-2.0.0.tar.gz +rm -r openjpeg-2.1.0 +tar -xvzf openjpeg-2.1.0.tar.gz -pushd openjpeg-2.0.0 +pushd openjpeg-2.1.0 cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install From bc2c7bee7018aab85cc5f7f29eaea267c863790c Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 30 May 2014 15:47:27 -0700 Subject: [PATCH 082/168] Defer initialization of openjpeg_version --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d385ba8a2..30d6e17d5 100644 --- a/setup.py +++ b/setup.py @@ -101,7 +101,7 @@ class pil_build_ext(build_ext): class feature: zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None - jpeg2000 = openjpeg_version = None + jpeg2000 = None required = [] def require(self, feat): @@ -383,6 +383,7 @@ class pil_build_ext(build_ext): _find_library_file(self, "libjpeg")): feature.jpeg = "libjpeg" # alternative name + feature.openjpeg_version = None if feature.want('jpeg2000'): best_version = None best_path = None From 89130203b1f583fecf398fb65ef42e3f8e1533db Mon Sep 17 00:00:00 2001 From: wiredfool Date: Sun, 1 Jun 2014 09:10:52 -0700 Subject: [PATCH 083/168] Update CHANGES.rst [ci-skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a8dd7d137..6b7d0162f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Added more ImageDraw tests + [hugovk] + - Added tests for Spider files [hugovk] From 27d49b6f27046221da93236963950c3bb2b07d77 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 2 Jun 2014 09:57:49 +0300 Subject: [PATCH 084/168] pep8 --- PIL/ImageCms.py | 225 ++++++++++++++++++++++++++++-------------------- 1 file changed, 132 insertions(+), 93 deletions(-) diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 5decaf704..fc176952e 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -66,7 +66,8 @@ pyCMS Added try/except statements arount type() checks of potential CObjects... Python won't let you use type() - on them, and raises a TypeError (stupid, if you ask me!) + on them, and raises a TypeError (stupid, if you ask + me!) Added buildProofTransformFromOpenProfiles() function. Additional fixes in DLL, see DLL code for details. @@ -115,7 +116,9 @@ FLAGS = { "MATRIXOUTPUT": 2, "MATRIXONLY": (1 | 2), "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot - "NOPRELINEARIZATION": 16, # Don't create prelinearization tables on precalculated transforms (internal use) + # Don't create prelinearization tables on precalculated transforms + # (internal use): + "NOPRELINEARIZATION": 16, "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) "NOTCACHE": 64, # Inhibit 1-pixel cache "NOTPRECALC": 256, @@ -168,13 +171,14 @@ class ImageCmsProfile: class ImageCmsTransform(Image.ImagePointHandler): + """Transform. This can be used with the procedural API, or with the standard Image.point() method. """ def __init__(self, input, output, input_mode, output_mode, - intent=INTENT_PERCEPTUAL, - proof=None, proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0): + intent=INTENT_PERCEPTUAL, proof=None, + proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0): if proof is None: self.transform = core.buildTransform( input.profile, output.profile, @@ -238,11 +242,15 @@ def get_display_profile(handle=None): # --------------------------------------------------------------------. class PyCMSError(Exception): - """ (pyCMS) Exception class. This is used for all errors in the pyCMS API. """ + + """ (pyCMS) Exception class. + This is used for all errors in the pyCMS API. """ pass -def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, outputMode=None, inPlace=0, flags=0): +def profileToProfile( + im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, + outputMode=None, inPlace=0, flags=0): """ (pyCMS) Applies an ICC transformation to a given image, mapping from inputProfile to outputProfile. @@ -264,29 +272,33 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER profiles, the input profile must handle RGB data, and the output profile must handle CMYK data. - :param im: An open PIL image object (i.e. Image.new(...) or Image.open(...), etc.) - :param inputProfile: String, as a valid filename path to the ICC input profile - you wish to use for this image, or a profile object + :param im: An open PIL image object (i.e. Image.new(...) or + Image.open(...), etc.) + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this image, or a profile object :param outputProfile: String, as a valid filename path to the ICC output profile you wish to use for this image, or a profile object - :param renderingIntent: Integer (0-3) specifying the rendering intent you wish - to use for the transform + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and what they do. - :param outputMode: A valid PIL mode for the output image (i.e. "RGB", "CMYK", - etc.). Note: if rendering the image "inPlace", outputMode MUST be the - same mode as the input, or omitted completely. If omitted, the outputMode - will be the same as the mode of the input image (im.mode) - :param inPlace: Boolean (1 = True, None or 0 = False). If True, the original - image is modified in-place, and None is returned. If False (default), a - new Image object is returned with the transform applied. + see the pyCMS documentation for details on rendering intents and what + they do. + :param outputMode: A valid PIL mode for the output image (i.e. "RGB", + "CMYK", etc.). Note: if rendering the image "inPlace", outputMode + MUST be the same mode as the input, or omitted completely. If + omitted, the outputMode will be the same as the mode of the input + image (im.mode) + :param inPlace: Boolean (1 = True, None or 0 = False). If True, the + original image is modified in-place, and None is returned. If False + (default), a new Image object is returned with the transform applied. :param flags: Integer (0-...) specifying additional flags - :returns: Either None or a new PIL image object, depending on value of inPlace + :returns: Either None or a new PIL image object, depending on value of + inPlace :exception PyCMSError: """ @@ -297,7 +309,8 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER raise PyCMSError("renderingIntent must be an integer between 0 and 3") if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) try: if not isinstance(inputProfile, ImageCmsProfile): @@ -329,8 +342,8 @@ def getOpenProfile(profileFilename): If profileFilename is not a vaild filename for an ICC profile, a PyCMSError will be raised. - :param profileFilename: String, as a valid filename path to the ICC profile you - wish to open, or a file-like object. + :param profileFilename: String, as a valid filename path to the ICC profile + you wish to open, or a file-like object. :returns: A CmsProfile class object. :exception PyCMSError: """ @@ -341,7 +354,9 @@ def getOpenProfile(profileFilename): raise PyCMSError(v) -def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, flags=0): +def buildTransform( + inputProfile, outputProfile, inMode, outMode, + renderingIntent=INTENT_PERCEPTUAL, flags=0): """ (pyCMS) Builds an ICC transform mapping from the inputProfile to the outputProfile. Use applyTransform to apply the transform to a given @@ -374,14 +389,14 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent manually overridden if you really want to, but I don't know of any time that would be of use, or would even work). - :param inputProfile: String, as a valid filename path to the ICC input profile - you wish to use for this transform, or a profile object + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object :param outputProfile: String, as a valid filename path to the ICC output profile you wish to use for this transform, or a profile object - :param inMode: String, as a valid PIL mode that the appropriate profile also - supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param outMode: String, as a valid PIL mode that the appropriate profile also - supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) :param renderingIntent: Integer (0-3) specifying the rendering intent you wish to use for the transform @@ -390,7 +405,8 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and what they do. + see the pyCMS documentation for details on rendering intents and what + they do. :param flags: Integer (0-...) specifying additional flags :returns: A CmsTransform class object. :exception PyCMSError: @@ -400,19 +416,26 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent raise PyCMSError("renderingIntent must be an integer between 0 and 3") if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) try: if not isinstance(inputProfile, ImageCmsProfile): inputProfile = ImageCmsProfile(inputProfile) if not isinstance(outputProfile, ImageCmsProfile): outputProfile = ImageCmsProfile(outputProfile) - return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags) + return ImageCmsTransform( + inputProfile, outputProfile, inMode, outMode, + renderingIntent, flags=flags) except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) -def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"]): +def buildProofTransform( + inputProfile, outputProfile, proofProfile, inMode, outMode, + renderingIntent=INTENT_PERCEPTUAL, + proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, + flags=FLAGS["SOFTPROOFING"]): """ (pyCMS) Builds an ICC transform mapping from the inputProfile to the outputProfile, but tries to simulate the result that would be @@ -451,17 +474,17 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo when the simulated device has a much wider gamut than the output device, you may obtain marginal results. - :param inputProfile: String, as a valid filename path to the ICC input profile - you wish to use for this transform, or a profile object + :param inputProfile: String, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object :param outputProfile: String, as a valid filename path to the ICC output (monitor, usually) profile you wish to use for this transform, or a profile object - :param proofProfile: String, as a valid filename path to the ICC proof profile - you wish to use for this transform, or a profile object - :param inMode: String, as a valid PIL mode that the appropriate profile also - supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param outMode: String, as a valid PIL mode that the appropriate profile also - supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param proofProfile: String, as a valid filename path to the ICC proof + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile + also supports (i.e. "RGB", "RGBA", "CMYK", etc.) :param renderingIntent: Integer (0-3) specifying the rendering intent you wish to use for the input->proof (simulated) transform @@ -470,7 +493,8 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and what they do. + see the pyCMS documentation for details on rendering intents and what + they do. :param proofRenderingIntent: Integer (0-3) specifying the rendering intent you wish to use for proof->output transform @@ -479,7 +503,8 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and what they do. + see the pyCMS documentation for details on rendering intents and what + they do. :param flags: Integer (0-...) specifying additional flags :returns: A CmsTransform class object. :exception PyCMSError: @@ -489,7 +514,8 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo raise PyCMSError("renderingIntent must be an integer between 0 and 3") if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + raise PyCMSError( + "flags must be an integer between 0 and %s" + _MAX_FLAG) try: if not isinstance(inputProfile, ImageCmsProfile): @@ -498,7 +524,9 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo outputProfile = ImageCmsProfile(outputProfile) if not isinstance(proofProfile, ImageCmsProfile): proofProfile = ImageCmsProfile(proofProfile) - return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags) + return ImageCmsTransform( + inputProfile, outputProfile, inMode, outMode, renderingIntent, + proofProfile, proofRenderingIntent, flags) except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -523,8 +551,8 @@ def applyTransform(im, transform, inPlace=0): is raised. This function applies a pre-calculated transform (from - ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an - image. The transform can be used for multiple images, saving + ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) + to an image. The transform can be used for multiple images, saving considerable calcuation time if doing the same conversion multiple times. If you want to modify im in-place instead of receiving a new image as @@ -537,10 +565,12 @@ def applyTransform(im, transform, inPlace=0): :param im: A PIL Image object, and im.mode must be the same as the inMode supported by the transform. :param transform: A valid CmsTransform class object - :param inPlace: Bool (1 == True, 0 or None == False). If True, im is modified - in place and None is returned, if False, a new Image object with the - transform applied is returned (and im is not changed). The default is False. - :returns: Either None, or a new PIL Image object, depending on the value of inPlace + :param inPlace: Bool (1 == True, 0 or None == False). If True, im is + modified in place and None is returned, if False, a new Image object + with the transform applied is returned (and im is not changed). The + default is False. + :returns: Either None, or a new PIL Image object, depending on the value of + inPlace :exception PyCMSError: """ @@ -572,24 +602,29 @@ def createProfile(colorSpace, colorTemp=-1): ImageCms.buildTransformFromOpenProfiles() to create a transform to apply to images. - :param colorSpace: String, the color space of the profile you wish to create. + :param colorSpace: String, the color space of the profile you wish to + create. Currently only "LAB", "XYZ", and "sRGB" are supported. :param colorTemp: Positive integer for the white point for the profile, in degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 - illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles, - and is ignored for XYZ and sRGB. + illuminant if omitted (5000k). colorTemp is ONLY applied to LAB + profiles, and is ignored for XYZ and sRGB. :returns: A CmsProfile class object :exception PyCMSError: """ if colorSpace not in ["LAB", "XYZ", "sRGB"]: - raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace) + raise PyCMSError( + "Color space not supported for on-the-fly profile creation (%s)" + % colorSpace) if colorSpace == "LAB": try: colorTemp = float(colorTemp) except: - raise PyCMSError("Color temperature must be numeric, \"%s\" not valid" % colorTemp) + raise PyCMSError( + "Color temperature must be numeric, \"%s\" not valid" + % colorTemp) try: return core.createProfile(colorSpace, colorTemp) @@ -611,10 +646,10 @@ def getProfileName(profile): profile was originally created. Sometimes this tag also contains additional information supplied by the creator. - :param profile: EITHER a valid CmsProfile object, OR a string of the filename - of an ICC profile. - :returns: A string containing the internal name of the profile as stored in an - ICC tag. + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal name of the profile as stored + in an ICC tag. :exception PyCMSError: """ @@ -653,10 +688,10 @@ def getProfileInfo(profile): info tag. This often contains details about the profile, and how it was created, as supplied by the creator. - :param profile: EITHER a valid CmsProfile object, OR a string of the filename - of an ICC profile. - :returns: A string containing the internal profile information stored in an ICC - tag. + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. :exception PyCMSError: """ @@ -664,7 +699,8 @@ def getProfileInfo(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) # add an extra newline to preserve pyCMS compatibility - # Python, not C. the white point bits weren't working well, so skipping. + # Python, not C. the white point bits weren't working well, + # so skipping. # // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint description = profile.profile.product_description cpright = profile.profile.product_copyright @@ -691,10 +727,10 @@ def getProfileCopyright(profile): Use this function to obtain the information stored in the profile's copyright tag. - :param profile: EITHER a valid CmsProfile object, OR a string of the filename - of an ICC profile. - :returns: A string containing the internal profile information stored in an ICC - tag. + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. :exception PyCMSError: """ try: @@ -713,16 +749,16 @@ def getProfileManufacturer(profile): If profile isn't a valid CmsProfile object or filename to a profile, a PyCMSError is raised. - If an error occurs while trying to obtain the manufacturer tag, a PyCMSError - is raised + If an error occurs while trying to obtain the manufacturer tag, a + PyCMSError is raised Use this function to obtain the information stored in the profile's manufacturer tag. - :param profile: EITHER a valid CmsProfile object, OR a string of the filename - of an ICC profile. - :returns: A string containing the internal profile information stored in an ICC - tag. + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. :exception PyCMSError: """ try: @@ -747,10 +783,10 @@ def getProfileModel(profile): Use this function to obtain the information stored in the profile's model tag. - :param profile: EITHER a valid CmsProfile object, OR a string of the filename - of an ICC profile. - :returns: A string containing the internal profile information stored in an ICC - tag. + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in + an ICC tag. :exception PyCMSError: """ @@ -776,10 +812,10 @@ def getProfileDescription(profile): Use this function to obtain the information stored in the profile's description tag. - :param profile: EITHER a valid CmsProfile object, OR a string of the filename - of an ICC profile. - :returns: A string containing the internal profile information stored in an ICC - tag. + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: A string containing the internal profile information stored in an + ICC tag. :exception PyCMSError: """ @@ -808,16 +844,18 @@ def getDefaultIntent(profile): If you wish to use a different intent than returned, use ImageCms.isIntentSupported() to verify it will work first. - :param profile: EITHER a valid CmsProfile object, OR a string of the filename - of an ICC profile. - :returns: Integer 0-3 specifying the default rendering intent for this profile. + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :returns: Integer 0-3 specifying the default rendering intent for this + profile. INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and what they do. + see the pyCMS documentation for details on rendering intents and what + they do. :exception PyCMSError: """ @@ -844,17 +882,18 @@ def isIntentSupported(profile, intent, direction): potential PyCMSError that will occur if they don't support the modes you select. - :param profile: EITHER a valid CmsProfile object, OR a string of the filename - of an ICC profile. - :param intent: Integer (0-3) specifying the rendering intent you wish to use - with this profile + :param profile: EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + :param intent: Integer (0-3) specifying the rendering intent you wish to + use with this profile INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and what they do. + see the pyCMS documentation for details on rendering intents and what + they do. :param direction: Integer specifing if the profile is to be used for input, output, or proof From e9830572947243d7d10d786b304804fee43b059d Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 2 Jun 2014 12:41:48 +0300 Subject: [PATCH 085/168] pep8/pyflakes --- Tests/test_imagecms.py | 84 +++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index dcb445c9f..8a7c172b2 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -9,12 +9,13 @@ except ImportError: SRGB = "Tests/icc/sRGB.icm" + def test_sanity(): # basic smoke test. # this mostly follows the cms_test outline. - v = ImageCms.versions() # should return four strings + v = ImageCms.versions() # should return four strings assert_equal(v[0], '1.0.0 pil') assert_equal(list(map(type, v)), [str, str, str, str]) @@ -41,12 +42,15 @@ def test_sanity(): assert_image(i, "RGB", (128, 128)) # test PointTransform convenience API - im = lena().point(t) + lena().point(t) + def test_name(): # get profile information for file assert_equal(ImageCms.getProfileName(SRGB).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') + + def x_test_info(): assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), ['sRGB IEC61966-2.1', '', @@ -54,11 +58,13 @@ def x_test_info(): 'WhitePoint : D65 (daylight)', '', 'Tests/icc/sRGB.icm']) + def test_intent(): assert_equal(ImageCms.getDefaultIntent(SRGB), 0) assert_equal(ImageCms.isIntentSupported( - SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + def test_profile_object(): # same, using profile object @@ -69,8 +75,9 @@ def test_profile_object(): # ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) assert_equal(ImageCms.getDefaultIntent(p), 0) assert_equal(ImageCms.isIntentSupported( - p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + def test_extensions(): # extensions @@ -79,12 +86,21 @@ def test_extensions(): assert_equal(ImageCms.getProfileName(p).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') + def test_exceptions(): # the procedural pyCMS API uses PyCMSError for all sorts of errors - assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) - assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) - assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) - assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None)) + assert_exception( + ImageCms.PyCMSError, + lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) + assert_exception( + ImageCms.PyCMSError, + lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) + assert_exception( + ImageCms.PyCMSError, + lambda: ImageCms.getProfileName(None)) + assert_exception( + ImageCms.PyCMSError, + lambda: ImageCms.isIntentSupported(SRGB, None, None)) def test_display_profile(): @@ -93,61 +109,63 @@ def test_display_profile(): def test_lab_color_profile(): - pLab = ImageCms.createProfile("LAB", 5000) - pLab = ImageCms.createProfile("LAB", 6500) + ImageCms.createProfile("LAB", 5000) + ImageCms.createProfile("LAB", 6500) + def test_simple_lab(): - i = Image.new('RGB', (10,10), (128,128,128)) + i = Image.new('RGB', (10, 10), (128, 128, 128)) - pLab = ImageCms.createProfile("LAB") + pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") i_lab = ImageCms.applyTransform(i, t) - assert_equal(i_lab.mode, 'LAB') - k = i_lab.getpixel((0,0)) - assert_equal(k, (137,128,128)) # not a linear luminance map. so L != 128 + k = i_lab.getpixel((0, 0)) + assert_equal(k, (137, 128, 128)) # not a linear luminance map. so L != 128 - L = i_lab.getdata(0) + L = i_lab.getdata(0) a = i_lab.getdata(1) b = i_lab.getdata(2) - assert_equal(list(L), [137]*100) - assert_equal(list(a), [128]*100) - assert_equal(list(b), [128]*100) + assert_equal(list(L), [137] * 100) + assert_equal(list(a), [128] * 100) + assert_equal(list(b), [128] * 100) + - def test_lab_color(): - pLab = ImageCms.createProfile("LAB") + pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - # need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType, - # and have that mapping work back to a PIL mode. (likely RGB) + # Need to add a type mapping for some PIL type to TYPE_Lab_8 in + # findLCMSType, and have that mapping work back to a PIL mode (likely RGB). i = ImageCms.applyTransform(lena(), t) assert_image(i, "LAB", (128, 128)) - - # i.save('temp.lab.tif') # visually verified vs PS. + + # i.save('temp.lab.tif') # visually verified vs PS. target = Image.open('Tests/images/lena.Lab.tif') assert_image_similar(i, target, 30) + def test_lab_srgb(): - pLab = ImageCms.createProfile("LAB") + pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") img = Image.open('Tests/images/lena.Lab.tif') img_srgb = ImageCms.applyTransform(img, t) - # img_srgb.save('temp.srgb.tif') # visually verified vs ps. - + # img_srgb.save('temp.srgb.tif') # visually verified vs ps. + assert_image_similar(lena(), img_srgb, 30) + def test_lab_roundtrip(): - # check to see if we're at least internally consistent. - pLab = ImageCms.createProfile("LAB") + # check to see if we're at least internally consistent. + pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") @@ -156,5 +174,3 @@ def test_lab_roundtrip(): out = ImageCms.applyTransform(i, t2) assert_image_similar(lena(), out, 2) - - From 9e984a19b11e7e7ba7826550eccff4c5d97be576 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 2 Jun 2014 13:00:21 +0300 Subject: [PATCH 086/168] Add more tests for ImageCms --- Tests/test_imagecms.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 8a7c172b2..5b3859c60 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -25,6 +25,10 @@ def test_sanity(): i = ImageCms.profileToProfile(lena(), SRGB, SRGB) assert_image(i, "RGB", (128, 128)) + i = lena() + ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) + assert_image(i, "RGB", (128, 128)) + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) @@ -51,12 +55,25 @@ def test_name(): 'IEC 61966-2.1 Default RGB colour space - sRGB') -def x_test_info(): +def test_info(): assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), ['sRGB IEC61966-2.1', '', - 'Copyright (c) 1998 Hewlett-Packard Company', '', - 'WhitePoint : D65 (daylight)', '', - 'Tests/icc/sRGB.icm']) + 'Copyright (c) 1998 Hewlett-Packard Company', '']) + + +def test_copyright(): + assert_equal(ImageCms.getProfileCopyright(SRGB).strip(), + 'Copyright (c) 1998 Hewlett-Packard Company') + + +def test_manufacturer(): + assert_equal(ImageCms.getProfileManufacturer(SRGB).strip(), + 'IEC http://www.iec.ch') + + +def test_description(): + assert_equal(ImageCms.getProfileDescription(SRGB).strip(), + 'sRGB IEC61966-2.1') def test_intent(): From 6115d348b983118bd43fb7c54e8d0a25d2c6fca2 Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 2 Jun 2014 13:19:01 +0300 Subject: [PATCH 087/168] Add more tests for ImageCms --- Tests/test_imagecms.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 5b3859c60..f52101eb1 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -33,6 +33,11 @@ def test_sanity(): i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) + i = lena() + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + ImageCms.applyTransform(lena(), t, inPlace=True) + assert_image(i, "RGB", (128, 128)) + p = ImageCms.createProfile("sRGB") o = ImageCms.getOpenProfile(SRGB) t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") @@ -71,6 +76,11 @@ def test_manufacturer(): 'IEC http://www.iec.ch') +def test_model(): + assert_equal(ImageCms.getProfileModel(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + def test_description(): assert_equal(ImageCms.getProfileDescription(SRGB).strip(), 'sRGB IEC61966-2.1') From beedca78e3c33e5e4c50d2a7389e6ea6762a0dea Mon Sep 17 00:00:00 2001 From: hugovk Date: Mon, 2 Jun 2014 15:57:50 +0300 Subject: [PATCH 088/168] Show counts of pep8/pyflakes after build --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 36dae5b7a..1d313a088 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ after_success: - coverage report - coveralls - pip install pep8 pyflakes - - pep8 PIL/*.py - - pyflakes PIL/*.py - - pep8 Tests/*.py - - pyflakes Tests/*.py + - pep8 --statistics --count PIL/*.py + - pep8 --statistics --count Tests/*.py + - pyflakes PIL/*.py | tee >(wc -l) + - pyflakes Tests/*.py | tee >(wc -l) From e944297e80b93aae86769830e4dc9f80a6bde66a Mon Sep 17 00:00:00 2001 From: joaoxsouls Date: Sun, 1 Jun 2014 23:57:25 +0100 Subject: [PATCH 089/168] add FreeBSD10 support to doc --- docs/installation.rst | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 94054df82..76c7d4ef5 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -67,11 +67,11 @@ Many of Pillow's features require external libraries: * Pillow has been tested with version **0.1.3**, which does not read transparent webp files. Versions **0.3.0** and **0.4.0** support - transparency. + transparency. -* **tcl/tk** provides support for tkinter bitmap and photo images. +* **tcl/tk** provides support for tkinter bitmap and photo images. -* **openjpeg** provides JPEG 2000 functionality. +* **openjpeg** provides JPEG 2000 functionality. * Pillow has been tested with openjpeg **2.0.0**. @@ -108,7 +108,7 @@ Or for Python 3:: $ sudo apt-get install python3-dev python3-setuptools In Fedora, the command is:: - + $ sudo yum install python-devel Prerequisites are installed on **Ubuntu 10.04 LTS** with:: @@ -185,6 +185,25 @@ to a specific version: $ pip install --use-wheel Pillow==2.3.0 +FreeBSD installation +--------------------- + +.. Note:: Only FreeBSD 10 tested + + +Make sure you have Python's development libraries installed.:: + + $ sudo pkg install python2 + +Or for Python 3:: + + $ sudo pkg install python3 + +Prerequisites are installed on **FreeBSD 10** with:: + + $ sudo pkg install jpeg tiff webp lcms2 freetype2 + + Platform support ---------------- @@ -199,7 +218,7 @@ current versions of Linux, OS X, and Windows. Contributors please test on your platform, edit this document, and send a pull request. -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ |**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | @@ -224,6 +243,8 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| FreeBSD 10 |Yes | 2.7,3.4 | 2.4,2.3.1 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | @@ -232,4 +253,3 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.3.0, 2.4.0 |x86,x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ - From c05dc710757a09a1a7228c92e25659d6f70b1947 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 2 Jun 2014 12:22:45 -0700 Subject: [PATCH 090/168] Update docs for OpenJPEG version --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 94054df82..0a6fcc6fd 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -73,7 +73,7 @@ Many of Pillow's features require external libraries: * **openjpeg** provides JPEG 2000 functionality. - * Pillow has been tested with openjpeg **2.0.0**. + * Pillow has been tested with openjpeg **2.0.0** and **2.1.0**. If the prerequisites are installed in the standard library locations for your machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration From 9dc2346dead9853edec57514cdd99ccf4898d0e8 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 3 Jun 2014 11:06:27 +0300 Subject: [PATCH 091/168] Fix Travis CI badge for new python-pillow name --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 277c5d01f..33b9813d0 100644 --- a/README.rst +++ b/README.rst @@ -5,8 +5,8 @@ Pillow Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. -.. image:: https://travis-ci.org/python-imaging/Pillow.svg?branch=master - :target: https://travis-ci.org/python-imaging/Pillow +.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master + :target: https://travis-ci.org/python-pillow/Pillow :alt: Travis CI build status .. image:: https://pypip.in/v/Pillow/badge.png From c9a4272af62f96974a1651c0fe877337aec79bc9 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 3 Jun 2014 13:02:44 +0300 Subject: [PATCH 092/168] Replace python-imaging with python-pillow (but yet not Coveralls) --- CHANGES.rst | 38 ++++++++++++++++---------------- PIL/TiffImagePlugin.py | 20 ++++++++--------- Tests/test_file_gif.py | 16 +++++++------- Tests/test_file_jpeg.py | 6 ++--- Tests/test_file_libtiff.py | 38 ++++++++++++++++---------------- Tests/test_file_png.py | 10 ++++----- Tests/test_file_tiff_metadata.py | 16 +++++++------- Tests/test_image_convert.py | 4 ++-- Tests/test_image_getpixel.py | 10 ++++----- Tests/test_image_point.py | 4 ++-- Tests/test_image_transform.py | 38 ++++++++++++++++---------------- Tests/test_imagefile.py | 6 ++--- Tests/test_locale.py | 4 ++-- Tests/test_numpy.py | 14 ++++++------ docs/_templates/sidebarhelp.html | 2 +- docs/about.rst | 6 ++--- docs/index.rst | 8 +++---- setup.py | 8 +++---- 18 files changed, 124 insertions(+), 124 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6b7d0162f..fbe2d3b0c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,34 +8,34 @@ Changelog (Pillow) - Added tests for Spider files [hugovk] - + - Use libtiff to write any compressed tiff files [wiredfool] - + - Support for pickling Image objects [hugovk] - Fixed resolution handling for EPS thumbnails [eliempje] - + - Fixed rendering of some binary EPS files (Issue #302) - [eliempje] - + [eliempje] + - Rename variables not to use built-in function names [hugovk] - -- Ignore junk JPEG markers + +- Ignore junk JPEG markers [hugovk] - + - Change default interpolation for Image.thumbnail to Image.ANTIALIAS [hugovk] - + - Add tests and fixes for saving PDFs [hugovk] - + - Remove transparency resource after P->RGBA conversion [hugovk] - + - Clean up preprocessor cruft for Windows [CounterPillow] @@ -45,13 +45,13 @@ Changelog (Pillow) - Added Image.close, context manager support. [wiredfool] -- Added support for 16 bit PGM files. +- Added support for 16 bit PGM files. [wiredfool] - Updated OleFileIO to version 0.30 from upstream [hugovk] -- Added support for additional TIFF floating point format +- Added support for additional TIFF floating point format [Hijackal] - Have the tempfile use a suffix with a dot @@ -81,7 +81,7 @@ Changelog (Pillow) - Added support for JPEG 2000 [al45tair] -- Add more detailed error messages to Image.py +- Add more detailed error messages to Image.py [larsmans] - Avoid conflicting _expand functions in PIL & MINGW, fixes #538 @@ -109,7 +109,7 @@ Changelog (Pillow) [wiredfool] - Fixed palette handling when converting from mode P->RGB->P - [d_schmidt] + [d_schmidt] - Fixed saving mode P image as a PNG with transparency = palette color 0 [d-schmidt] @@ -119,7 +119,7 @@ Changelog (Pillow) - Fixed DOS with invalid palette size or invalid image size in BMP file [wiredfool] - + - Added support for BMP version 4 and 5 [eddwardo, wiredfool] @@ -152,7 +152,7 @@ Changelog (Pillow) - Prefer homebrew freetype over X11 freetype (but still allow both) [dmckeone] - + 2.3.1 (2014-03-14) ------------------ @@ -277,7 +277,7 @@ Changelog (Pillow) [nikmolnar] - Fix for encoding of b_whitespace, similar to closed issue #272 - [mhogg] + [mhogg] - Fix #273: Add numpy array interface support for 16 and 32 bit integer modes [cgohlke] @@ -437,7 +437,7 @@ Changelog (Pillow) - Add Python 3 support. (Pillow >= 2.0.0 supports Python 2.6, 2.7, 3.2, 3.3. Pillow < 2.0.0 supports Python 2.4, 2.5, 2.6, 2.7.) [fluggo] -- Add PyPy support (experimental, please see: https://github.com/python-imaging/Pillow/issues/67) +- Add PyPy support (experimental, please see: https://github.com/python-pillow/Pillow/issues/67) - Add WebP support. [lqs] diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 11b92747c..34cb020aa 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -54,7 +54,7 @@ import collections import itertools import os -# Set these to true to force use of libtiff for reading or writing. +# Set these to true to force use of libtiff for reading or writing. READ_LIBTIFF = False WRITE_LIBTIFF= False @@ -238,7 +238,7 @@ class ImageFileDirectory(collections.MutableMapping): Value: integer corresponding to the data type from `TiffTags.TYPES` - 'internal' + 'internal' * self.tags = {} Key: numerical tiff tag number Value: Decoded data, Generally a tuple. * If set from __setval__ -- always a tuple @@ -489,10 +489,10 @@ class ImageFileDirectory(collections.MutableMapping): if tag in self.tagtype: typ = self.tagtype[tag] - + if Image.DEBUG: print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) - + if typ == 1: # byte data if isinstance(value, tuple): @@ -512,7 +512,7 @@ class ImageFileDirectory(collections.MutableMapping): # and doesn't match the tiff spec: 8-bit byte that # contains a 7-bit ASCII code; the last byte must be # NUL (binary zero). Also, I don't think this was well - # excersized before. + # excersized before. data = value = b"" + value.encode('ascii', 'replace') + b"\0" else: # integer data @@ -859,7 +859,7 @@ class TiffImageFile(ImageFile.ImageFile): # libtiff handles the fillmode for us, so 1;IR should # actually be 1;I. Including the R double reverses the # bits, so stripes of the image are reversed. See - # https://github.com/python-imaging/Pillow/issues/279 + # https://github.com/python-pillow/Pillow/issues/279 if fillorder == 2: key = ( self.tag.prefix, photo, format, 1, @@ -984,11 +984,11 @@ def _save(im, fp, filename): compression = im.encoderinfo.get('compression',im.info.get('compression','raw')) - libtiff = WRITE_LIBTIFF or compression != 'raw' + libtiff = WRITE_LIBTIFF or compression != 'raw' # required for color libtiff images ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) - + # -- multi-page -- skip TIFF header on subsequent pages if not libtiff and fp.tell() == 0: # tiff header (write via IFD to get everything right) @@ -1025,7 +1025,7 @@ def _save(im, fp, filename): # which support profiles as TIFF) -- 2008-06-06 Florian Hoech if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] - + if "description" in im.encoderinfo: ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] if "resolution" in im.encoderinfo: @@ -1091,7 +1091,7 @@ def _save(im, fp, filename): blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes. atts={} - # bits per sample is a single short in the tiff directory, not a list. + # bits per sample is a single short in the tiff directory, not a list. atts[BITSPERSAMPLE] = bits[0] # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 4318e178e..566baa200 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -37,7 +37,7 @@ def test_roundtrip(): assert_image_similar(reread.convert('RGB'), im, 50) def test_roundtrip2(): - #see https://github.com/python-imaging/Pillow/issues/403 + #see https://github.com/python-pillow/Pillow/issues/403 out = tempfile('temp.gif') im = Image.open('Images/lena.gif') im2 = im.copy() @@ -48,11 +48,11 @@ def test_roundtrip2(): def test_palette_handling(): - # see https://github.com/python-imaging/Pillow/issues/513 + # see https://github.com/python-pillow/Pillow/issues/513 im = Image.open('Images/lena.gif') im = im.convert('RGB') - + im = im.resize((100,100), Image.ANTIALIAS) im2 = im.convert('P', palette=Image.ADAPTIVE, colors=256) @@ -60,11 +60,11 @@ def test_palette_handling(): im2.save(f, optimize=True) reloaded = Image.open(f) - + assert_image_similar(im, reloaded.convert('RGB'), 10) - + def test_palette_434(): - # see https://github.com/python-imaging/Pillow/issues/434 + # see https://github.com/python-pillow/Pillow/issues/434 def roundtrip(im, *args, **kwargs): out = tempfile('temp.gif') @@ -78,10 +78,10 @@ def test_palette_434(): assert_image_equal(*roundtrip(im)) assert_image_equal(*roundtrip(im, optimize=True)) - + im = im.convert("RGB") # check automatic P conversion reloaded = roundtrip(im)[1].convert('RGB') assert_image_equal(im, reloaded) - + diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 4871c3fbf..43ed55969 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -126,7 +126,7 @@ def test_optimize(): def test_optimize_large_buffer(): - # https://github.com/python-imaging/Pillow/issues/148 + # https://github.com/python-pillow/Pillow/issues/148 f = tempfile('temp.jpg') # this requires ~ 1.5x Image.MAXBLOCK im = Image.new("RGB", (4096, 4096), 0xff3333) @@ -159,7 +159,7 @@ def test_progressive_large_buffer_highest_quality(): def test_large_exif(): - # https://github.com/python-imaging/Pillow/issues/148 + # https://github.com/python-pillow/Pillow/issues/148 f = tempfile('temp.jpg') im = lena() im.save(f, 'JPEG', quality=90, exif=b"1"*65532) @@ -231,7 +231,7 @@ def test_quality_keep(): def test_junk_jpeg_header(): - # https://github.com/python-imaging/Pillow/issues/630 + # https://github.com/python-pillow/Pillow/issues/630 filename = "Tests/images/junk_jpeg_header.jpg" assert_no_exception(lambda: Image.open(filename)) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 58fa75239..fddff443a 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -71,7 +71,7 @@ def test_g4_eq_png(): assert_image_equal(g4, png) -# see https://github.com/python-imaging/Pillow/issues/279 +# see https://github.com/python-pillow/Pillow/issues/279 def test_g4_fillorder_eq_png(): """ Checking that we're actually getting the data that we expect""" png = Image.open('Tests/images/g4-fillorder-test.png') @@ -96,7 +96,7 @@ def test_g4_write(): assert_equal(reread.info['compression'], 'group4') assert_equal(reread.info['compression'], orig.info['compression']) - + assert_false(orig.tobytes() == reread.tobytes()) def test_adobe_deflate_tiff(): @@ -120,7 +120,7 @@ def test_write_metadata(): original = img.tag.named() reloaded = loaded.tag.named() - # PhotometricInterpretation is set from SAVE_INFO, not the original image. + # PhotometricInterpretation is set from SAVE_INFO, not the original image. ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'PhotometricInterpretation'] for tag, value in reloaded.items(): @@ -133,7 +133,7 @@ def test_write_metadata(): assert_equal(original[tag], value, "%s didn't roundtrip" % tag) for tag, value in original.items(): - if tag not in ignored: + if tag not in ignored: if tag.endswith('Resolution'): val = reloaded[tag] assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1], @@ -164,7 +164,7 @@ def test_little_endian(): else: assert_equal(b[0], b'\xe0') assert_equal(b[1], b'\x01') - + out = tempfile("temp.tif") #out = "temp.le.tif" @@ -174,8 +174,8 @@ def test_little_endian(): assert_equal(reread.info['compression'], im.info['compression']) assert_equal(reread.getpixel((0,0)), 480) # UNDONE - libtiff defaults to writing in native endian, so - # on big endian, we'll get back mode = 'I;16B' here. - + # on big endian, we'll get back mode = 'I;16B' here. + def test_big_endian(): im = Image.open('Tests/images/16bit.MM.deflate.tif') @@ -191,7 +191,7 @@ def test_big_endian(): else: assert_equal(b[0], b'\x01') assert_equal(b[1], b'\xe0') - + out = tempfile("temp.tif") im.save(out) reread = Image.open(out) @@ -203,12 +203,12 @@ def test_g4_string_info(): """Tests String data in info directory""" file = "Tests/images/lena_g4_500.tif" orig = Image.open(file) - + out = tempfile("temp.tif") orig.tag[269] = 'temp.tif' orig.save(out) - + reread = Image.open(out) assert_equal('temp.tif', reread.tag[269]) @@ -223,8 +223,8 @@ def test_12bit_rawmode(): # convert 12bit.cropped.tif -depth 16 tmp.tif # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, - # so we need to unshift so that the integer values are the same. - + # so we need to unshift so that the integer values are the same. + im2 = Image.open('Tests/images/12in16bit.tif') if Image.DEBUG: @@ -235,11 +235,11 @@ def test_12bit_rawmode(): print (im2.getpixel((0,0))) print (im2.getpixel((0,1))) print (im2.getpixel((0,2))) - + assert_image_equal(im, im2) def test_blur(): - # test case from irc, how to do blur on b/w image and save to compressed tif. + # test case from irc, how to do blur on b/w image and save to compressed tif. from PIL import ImageFilter out = tempfile('temp.tif') im = Image.open('Tests/images/pport_g4.tif') @@ -266,7 +266,7 @@ def test_compressions(): im.save(out, compression='jpeg') im2 = Image.open(out) assert_image_similar(im, im2, 30) - + def test_cmyk_save(): im = lena('CMYK') @@ -280,7 +280,7 @@ def xtest_bw_compression_wRGB(): """ This test passes, but when running all tests causes a failure due to output on stderr from the error thrown by libtiff. We need to capture that but not now""" - + im = lena('RGB') out = tempfile('temp.tif') @@ -293,8 +293,8 @@ def test_fp_leak(): fn = im.fp.fileno() assert_no_exception(lambda: os.fstat(fn)) - im.load() # this should close it. - assert_exception(OSError, lambda: os.fstat(fn)) + im.load() # this should close it. + assert_exception(OSError, lambda: os.fstat(fn)) im = None # this should force even more closed. - assert_exception(OSError, lambda: os.fstat(fn)) + assert_exception(OSError, lambda: os.fstat(fn)) assert_exception(OSError, lambda: os.close(fn)) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index c17829194..0b0ad3ca7 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -101,7 +101,7 @@ def test_bad_text(): assert_equal(im.info, {'spam': 'egg\x00'}) def test_bad_ztxt(): - # Test reading malformed zTXt chunks (python-imaging/Pillow#318) + # Test reading malformed zTXt chunks (python-pillow/Pillow#318) im = load(HEAD + chunk(b'zTXt') + TAIL) assert_equal(im.info, {}) @@ -182,7 +182,7 @@ def test_save_l_transparency(): file = tempfile("temp.png") assert_no_exception(lambda: im.save(file)) - # There are 559 transparent pixels. + # There are 559 transparent pixels. im = im.convert('RGBA') assert_equal(im.split()[3].getcolors()[0][0], 559) @@ -255,7 +255,7 @@ def test_trns_p(): # Check writing a transparency of 0, issue #528 im = lena('P') im.info['transparency']=0 - + f = tempfile("temp.png") im.save(f) @@ -263,8 +263,8 @@ def test_trns_p(): assert_true('transparency' in im2.info) assert_image_equal(im2.convert('RGBA'), im.convert('RGBA')) - - + + def test_save_icc_profile_none(): # check saving files with an ICC profile set to None (omit profile) in_file = "Tests/images/icc_profile_none.png" diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 354eb972f..c759d8c0d 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -6,9 +6,9 @@ tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys())) def test_rt_metadata(): """ Test writing arbitray metadata into the tiff image directory Use case is ImageJ private tags, one numeric, one arbitrary - data. https://github.com/python-imaging/Pillow/issues/291 + data. https://github.com/python-pillow/Pillow/issues/291 """ - + img = lena() textdata = "This is some arbitrary metadata for a text field" @@ -20,15 +20,15 @@ def test_rt_metadata(): f = tempfile("temp.tif") img.save(f, tiffinfo=info) - + loaded = Image.open(f) assert_equal(loaded.tag[50838], (len(textdata),)) assert_equal(loaded.tag[50839], textdata) - + def test_read_metadata(): img = Image.open('Tests/images/lena_g4.tif') - + known = {'YResolution': ((1207959552, 16777216),), 'PlanarConfiguration': (1,), 'BitsPerSample': (1,), @@ -48,7 +48,7 @@ def test_read_metadata(): 'StripOffsets': (8,), 'Software': 'ImageMagick 6.5.7-8 2012-08-17 Q16 http://www.imagemagick.org'} - # assert_equal is equivalent, but less helpful in telling what's wrong. + # assert_equal is equivalent, but less helpful in telling what's wrong. named = img.tag.named() for tag, value in named.items(): assert_equal(known[tag], value) @@ -70,11 +70,11 @@ def test_write_metadata(): reloaded = loaded.tag.named() ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets'] - + for tag, value in reloaded.items(): if tag not in ignored: assert_equal(original[tag], value, "%s didn't roundtrip" % tag) for tag, value in original.items(): - if tag not in ignored: + if tag not in ignored: assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 6a39b0e3b..ce7412e94 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -28,7 +28,7 @@ def test_default(): assert_image(im, "RGB", im.size) -# ref https://github.com/python-imaging/Pillow/issues/274 +# ref https://github.com/python-pillow/Pillow/issues/274 def _test_float_conversion(im): orig = im.getpixel((5, 5)) @@ -76,7 +76,7 @@ def test_trns_p(): assert_no_exception(lambda: rgb.save(f)) -# ref https://github.com/python-imaging/Pillow/issues/664 +# ref https://github.com/python-pillow/Pillow/issues/664 def test_trns_p_rgba(): # Arrange diff --git a/Tests/test_image_getpixel.py b/Tests/test_image_getpixel.py index 67f5904a2..da3d8115e 100644 --- a/Tests/test_image_getpixel.py +++ b/Tests/test_image_getpixel.py @@ -16,27 +16,27 @@ def color(mode): def check(mode, c=None): if not c: c = color(mode) - + #check putpixel im = Image.new(mode, (1, 1), None) im.putpixel((0, 0), c) assert_equal(im.getpixel((0, 0)), c, "put/getpixel roundtrip failed for mode %s, color %s" % (mode, c)) - + # check inital color im = Image.new(mode, (1, 1), c) assert_equal(im.getpixel((0, 0)), c, "initial color failed for mode %s, color %s " % (mode, color)) -def test_basic(): +def test_basic(): for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F", "P", "PA", "RGB", "RGBA", "RGBX", "CMYK","YCbCr"): check(mode) def test_signedness(): - # see https://github.com/python-imaging/Pillow/issues/452 + # see https://github.com/python-pillow/Pillow/issues/452 # pixelaccess is using signed int* instead of uint* for mode in ("I;16", "I;16B"): check(mode, 2**15-1) @@ -44,6 +44,6 @@ def test_signedness(): check(mode, 2**15+1) check(mode, 2**16-1) - + diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 34233f80e..4fc11ca05 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -4,7 +4,7 @@ from PIL import Image if hasattr(sys, 'pypy_version_info'): # This takes _forever_ on pypy. Open Bug, - # see https://github.com/python-imaging/Pillow/issues/484 + # see https://github.com/python-pillow/Pillow/issues/484 skip() def test_sanity(): @@ -26,7 +26,7 @@ def test_sanity(): def test_16bit_lut(): """ Tests for 16 bit -> 8 bit lut for converting I->L images - see https://github.com/python-imaging/Pillow/issues/440 + see https://github.com/python-pillow/Pillow/issues/440 """ im = lena("I") diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index fdee6072f..dd9b6fe5c 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -10,9 +10,9 @@ def test_extent(): w//2,h//2), # ul -> lr Image.BILINEAR) - + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) - + assert_image_similar(transformed, scaled, 10) # undone -- precision? def test_quad(): @@ -23,9 +23,9 @@ def test_quad(): (0,0,0,h//2, w//2,h//2,w//2,0), # ul -> ccw around quad Image.BILINEAR) - + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) - + assert_image_equal(transformed, scaled) def test_mesh(): @@ -48,8 +48,8 @@ def test_mesh(): checker = Image.new('RGBA', im.size) checker.paste(scaled, (0,0)) checker.paste(scaled, (w//2,h//2)) - - assert_image_equal(transformed, checker) + + assert_image_equal(transformed, checker) # now, check to see that the extra area is (0,0,0,0) blank = Image.new('RGBA', (w//2,h//2), (0,0,0,0)) @@ -59,34 +59,34 @@ def test_mesh(): def _test_alpha_premult(op): # create image with half white, half black, with the black half transparent. - # do op, + # do op, # there should be no darkness in the white section. im = Image.new('RGBA', (10,10), (0,0,0,0)); im2 = Image.new('RGBA', (5,10), (255,255,255,255)); im.paste(im2, (0,0)) - + im = op(im, (40,10)) im_background = Image.new('RGB', (40,10), (255,255,255)) im_background.paste(im, (0,0), im) - + hist = im_background.histogram() assert_equal(40*10, hist[-1]) - + def test_alpha_premult_resize(): def op (im, sz): return im.resize(sz, Image.LINEAR) - + _test_alpha_premult(op) - + def test_alpha_premult_transform(): - + def op(im, sz): (w,h) = im.size return im.transform(sz, Image.EXTENT, (0,0, - w,h), + w,h), Image.BILINEAR) _test_alpha_premult(op) @@ -94,7 +94,7 @@ def test_alpha_premult_transform(): def test_blank_fill(): # attempting to hit - # https://github.com/python-imaging/Pillow/issues/254 reported + # https://github.com/python-pillow/Pillow/issues/254 reported # # issue is that transforms with transparent overflow area # contained junk from previous images, especially on systems with @@ -106,11 +106,11 @@ def test_blank_fill(): # # Running by default, but I'd totally understand not doing it in # the future - - foo = [Image.new('RGBA',(1024,1024), (a,a,a,a)) - for a in range(1,65)] - # Yeah. Watch some JIT optimize this out. + foo = [Image.new('RGBA',(1024,1024), (a,a,a,a)) + for a in range(1,65)] + + # Yeah. Watch some JIT optimize this out. foo = None test_mesh() diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index adf282b03..e3992828a 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -49,14 +49,14 @@ def test_parser(): if EpsImagePlugin.has_ghostscript(): im1, im2 = roundtrip("EPS") - assert_image_similar(im1, im2.convert('L'),20) # EPS comes back in RGB - + assert_image_similar(im1, im2.convert('L'),20) # EPS comes back in RGB + if "jpeg_encoder" in codecs: im1, im2 = roundtrip("JPEG") # lossy compression assert_image(im1, im2.mode, im2.size) # XXX Why assert exception and why does it fail? - # https://github.com/python-imaging/Pillow/issues/78 + # https://github.com/python-pillow/Pillow/issues/78 #assert_exception(IOError, lambda: roundtrip("PDF")) def test_ico(): diff --git a/Tests/test_locale.py b/Tests/test_locale.py index 2683c561b..6b2b95201 100644 --- a/Tests/test_locale.py +++ b/Tests/test_locale.py @@ -3,7 +3,7 @@ from PIL import Image import locale -# ref https://github.com/python-imaging/Pillow/issues/272 +# ref https://github.com/python-pillow/Pillow/issues/272 ## on windows, in polish locale: ## import locale @@ -16,7 +16,7 @@ import locale ## 7 ## 160 -# one of string.whitespace is not freely convertable into ascii. +# one of string.whitespace is not freely convertable into ascii. path = "Images/lena.jpg" diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 4833cabb2..9b7881c23 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -83,12 +83,12 @@ def test_16bit(): def test_to_array(): def _to_array(mode, dtype): - img = lena(mode) + img = lena(mode) np_img = numpy.array(img) _test_img_equals_nparray(img, np_img) assert_equal(np_img.dtype, numpy.dtype(dtype)) - - + + modes = [("L", 'uint8'), ("I", 'int32'), ("F", 'float32'), @@ -101,20 +101,20 @@ def test_to_array(): ("I;16B", '>u2'), ("I;16L", ' If you've discovered a bug, you can - open an issue + open an issue on Github.

diff --git a/docs/about.rst b/docs/about.rst index e8c9356dc..817773610 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -11,8 +11,8 @@ The fork authors' goal is to foster active development of PIL through: - Regular releases to the `Python Package Index`_ - Solicitation for community contributions and involvement on `Image-SIG`_ -.. _Travis CI: https://travis-ci.org/python-imaging/Pillow -.. _GitHub: https://github.com/python-imaging/Pillow +.. _Travis CI: https://travis-ci.org/python-pillow/Pillow +.. _GitHub: https://github.com/python-pillow/Pillow .. _Python Package Index: https://pypi.python.org/pypi/Pillow .. _Image-SIG: http://mail.python.org/mailman/listinfo/image-sig @@ -60,6 +60,6 @@ announcement. So if you still want to support PIL, please .. _report issues here first: https://bitbucket.org/effbot/pil-2009-raclette/issues -.. _open the corresponding Pillow tickets here: https://github.com/python-imaging/Pillow/issues +.. _open the corresponding Pillow tickets here: https://github.com/python-pillow/Pillow/issues Please provide a link to the PIL ticket so we can track the issue(s) upstream. diff --git a/docs/index.rst b/docs/index.rst index 25e9f6b73..2eb845bff 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,8 +4,8 @@ Pillow Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. -.. image:: https://travis-ci.org/python-imaging/Pillow.svg?branch=master - :target: https://travis-ci.org/python-imaging/Pillow +.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master + :target: https://travis-ci.org/python-pillow/Pillow :alt: Travis CI build status .. image:: https://pypip.in/v/Pillow/badge.png @@ -24,7 +24,7 @@ To start using Pillow, please read the :doc:`installation instructions `. You can get the source and contribute at -https://github.com/python-imaging/Pillow. You can download archives +https://github.com/python-pillow/Pillow. You can download archives and old versions from `PyPI `_. .. toctree:: @@ -42,7 +42,7 @@ Support Pillow! PIL needs you! Please help us maintain the Python Imaging Library here: -- `GitHub `_ +- `GitHub `_ - `Freenode `_ - `Image-SIG `_ diff --git a/setup.py b/setup.py index 50ca985e3..83defd82b 100644 --- a/setup.py +++ b/setup.py @@ -234,7 +234,7 @@ class pil_build_ext(build_ext): elif sys.platform.startswith("linux"): arch_tp = (plat.processor(), plat.architecture()[0]) if arch_tp == ("x86_64","32bit"): - # 32 bit build on 64 bit machine. + # 32 bit build on 64 bit machine. _add_directory(library_dirs, "/usr/lib/i386-linux-gnu") else: for platform_ in arch_tp: @@ -339,7 +339,7 @@ class pil_build_ext(build_ext): # on Windows, look for the OpenJPEG libraries in the location that # the official installed puts them if sys.platform == "win32": - _add_directory(library_dirs, + _add_directory(library_dirs, os.path.join(os.environ.get("ProgramFiles", ""), "OpenJPEG 2.0", "lib")) _add_directory(include_dirs, @@ -378,7 +378,7 @@ class pil_build_ext(build_ext): if _find_include_file(self, "openjpeg-2.0/openjpeg.h"): if _find_library_file(self, "openjp2"): feature.jpeg2000 = "openjp2" - + if feature.want('tiff'): if _find_library_file(self, "tiff"): feature.tiff = "tiff" @@ -660,7 +660,7 @@ setup( _read('CHANGES.rst')).decode('utf-8'), author='Alex Clark (fork author)', author_email='aclark@aclark.net', - url='http://python-imaging.github.io/', + url='http://python-pillow.github.io/', classifiers=[ "Development Status :: 6 - Mature", "Topic :: Multimedia :: Graphics", From dc667b1f19b5dd85f97ebd14c7497eaaa218c229 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 3 Jun 2014 09:01:57 -0400 Subject: [PATCH 093/168] Fix link --- PIL/OleFileIO-README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/OleFileIO-README.md b/PIL/OleFileIO-README.md index 4a4fdcbca..11a0e9053 100644 --- a/PIL/OleFileIO-README.md +++ b/PIL/OleFileIO-README.md @@ -7,7 +7,7 @@ This is an improved version of the OleFileIO module from [PIL](http://www.python As far as I know, this module is now the most complete and robust Python implementation to read MS OLE2 files, portable on several operating systems. (please tell me if you know other similar Python modules) -OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-imaging.github.io/), the friendly fork of PIL. +OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-pillow.github.io/), the friendly fork of PIL. OleFileIO\_PL is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data, then please also check [python-oletools](http://www.decalage.info/python/oletools), which are built upon OleFileIO_PL. @@ -348,4 +348,4 @@ By obtaining, using, and/or copying this software and/or its associated document Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. -SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. From cd967680cb1314ecb45cc10ff246b5c6756eb93f Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 3 Jun 2014 18:04:27 +0300 Subject: [PATCH 094/168] Move dummy test to test/ and run with nosetests --- .travis.yml | 4 +++- {PIL => test}/tests.py | 0 2 files changed, 3 insertions(+), 1 deletion(-) rename {PIL => test}/tests.py (100%) diff --git a/.travis.yml b/.travis.yml index 36dae5b7a..17a0b0363 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ python: install: - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" - "pip install cffi" - - "pip install coveralls" + - "pip install coveralls nose" # webp - pushd depends && ./install_webp.sh && popd @@ -35,6 +35,8 @@ script: - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi + - nosetests test/ + after_success: - coverage report - coveralls diff --git a/PIL/tests.py b/test/tests.py similarity index 100% rename from PIL/tests.py rename to test/tests.py From 5c8c777ea4ac1a47efa32e1ee96a13b531351676 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 3 Jun 2014 22:38:56 +0300 Subject: [PATCH 095/168] Start moving tests over to use unittest --- .travis.yml | 3 +- Tests/test_image_frombytes.py | 10 - Tests/test_image_getim.py | 14 -- Tests/test_image_tobytes.py | 7 - test/test_image_frombytes.py | 18 ++ test/test_image_getim.py | 19 ++ test/test_image_tobytes.py | 13 ++ test/tester.py | 393 ++++++++++++++++++++++++++++++++++ test/tests.py | 8 +- 9 files changed, 450 insertions(+), 35 deletions(-) delete mode 100644 Tests/test_image_frombytes.py delete mode 100644 Tests/test_image_getim.py delete mode 100644 Tests/test_image_tobytes.py create mode 100644 test/test_image_frombytes.py create mode 100644 test/test_image_getim.py create mode 100644 test/test_image_tobytes.py create mode 100644 test/tester.py diff --git a/.travis.yml b/.travis.yml index 17a0b0363..1b864eecf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,12 +30,13 @@ script: # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python selftest.py; fi - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then nosetests test/; fi # Cover the others - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* -m nose test/; fi - - nosetests test/ after_success: - coverage report diff --git a/Tests/test_image_frombytes.py b/Tests/test_image_frombytes.py deleted file mode 100644 index aa157aa6a..000000000 --- a/Tests/test_image_frombytes.py +++ /dev/null @@ -1,10 +0,0 @@ -from tester import * - -from PIL import Image - -def test_sanity(): - im1 = lena() - im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes()) - - assert_image_equal(im1, im2) - diff --git a/Tests/test_image_getim.py b/Tests/test_image_getim.py deleted file mode 100644 index 8d2f12fc2..000000000 --- a/Tests/test_image_getim.py +++ /dev/null @@ -1,14 +0,0 @@ -from tester import * - -from PIL import Image - -def test_sanity(): - - im = lena() - type_repr = repr(type(im.getim())) - - if py3: - assert_true("PyCapsule" in type_repr) - - assert_true(isinstance(im.im.id, int)) - diff --git a/Tests/test_image_tobytes.py b/Tests/test_image_tobytes.py deleted file mode 100644 index d42399993..000000000 --- a/Tests/test_image_tobytes.py +++ /dev/null @@ -1,7 +0,0 @@ -from tester import * - -from PIL import Image - -def test_sanity(): - data = lena().tobytes() - assert_true(isinstance(data, bytes)) diff --git a/test/test_image_frombytes.py b/test/test_image_frombytes.py new file mode 100644 index 000000000..ee68b6722 --- /dev/null +++ b/test/test_image_frombytes.py @@ -0,0 +1,18 @@ +from tester import unittest, PillowTestCase, lena + +from PIL import Image + + +class TestImageFromBytes(PillowTestCase): + + def test_sanity(self): + im1 = lena() + im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes()) + + self.assert_image_equal(im1, im2) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_getim.py b/test/test_image_getim.py new file mode 100644 index 000000000..02744a529 --- /dev/null +++ b/test/test_image_getim.py @@ -0,0 +1,19 @@ +from tester import unittest, PillowTestCase, lena, py3 + + +class TestImageGetIm(PillowTestCase): + + def test_sanity(self): + im = lena() + type_repr = repr(type(im.getim())) + + if py3: + self.assertIn("PyCapsule", type_repr) + + self.assertIsInstance(im.im.id, int) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_tobytes.py b/test/test_image_tobytes.py new file mode 100644 index 000000000..ee7b4c9e6 --- /dev/null +++ b/test/test_image_tobytes.py @@ -0,0 +1,13 @@ +from tester import unittest, lena + + +class TestImageToBytes(unittest.TestCase): + + def test_sanity(self): + data = lena().tobytes() + self.assertTrue(isinstance(data, bytes)) + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/tester.py b/test/tester.py new file mode 100644 index 000000000..77b38a15a --- /dev/null +++ b/test/tester.py @@ -0,0 +1,393 @@ +""" +Helper functions. +""" +from __future__ import print_function +import sys + +if sys.version_info[:2] <= (2, 6): + import unittest2 as unittest +else: + import unittest + + +class PillowTestCase(unittest.TestCase): + + def assert_image_equal(self, a, b, msg=None): + self.assertEqual( + a.mode, b.mode, + msg or "got mode %r, expected %r" % (a.mode, b.mode)) + self.assertEqual( + a.size, b.size, + msg or "got size %r, expected %r" % (a.size, b.size)) + self.assertEqual( + a.tobytes(), b.tobytes(), + msg or "got different content") + + +# # require that deprecation warnings are triggered +# import warnings +# warnings.simplefilter('default') +# # temporarily turn off resource warnings that warn about unclosed +# # files in the test scripts. +# try: +# warnings.filterwarnings("ignore", category=ResourceWarning) +# except NameError: +# # we expect a NameError on py2.x, since it doesn't have ResourceWarnings. +# pass + +import sys +py3 = (sys.version_info >= (3, 0)) + +# # some test helpers +# +# _target = None +# _tempfiles = [] +# _logfile = None +# +# +# def success(): +# import sys +# success.count += 1 +# if _logfile: +# print(sys.argv[0], success.count, failure.count, file=_logfile) +# return True +# +# +# def failure(msg=None, frame=None): +# import sys +# import linecache +# failure.count += 1 +# if _target: +# if frame is None: +# frame = sys._getframe() +# while frame.f_globals.get("__name__") != _target.__name__: +# frame = frame.f_back +# location = (frame.f_code.co_filename, frame.f_lineno) +# prefix = "%s:%d: " % location +# line = linecache.getline(*location) +# print(prefix + line.strip() + " failed:") +# if msg: +# print("- " + msg) +# if _logfile: +# print(sys.argv[0], success.count, failure.count, file=_logfile) +# return False +# +# success.count = failure.count = 0 +# +# +# # predicates +# +# def assert_almost_equal(a, b, msg=None, eps=1e-6): +# if abs(a-b) < eps: +# success() +# else: +# failure(msg or "got %r, expected %r" % (a, b)) +# +# +# def assert_deep_equal(a, b, msg=None): +# try: +# if len(a) == len(b): +# if all([x == y for x, y in zip(a, b)]): +# success() +# else: +# failure(msg or "got %s, expected %s" % (a, b)) +# else: +# failure(msg or "got length %s, expected %s" % (len(a), len(b))) +# except: +# assert_equal(a, b, msg) +# +# +# def assert_greater(a, b, msg=None): +# if a > b: +# success() +# else: +# failure(msg or "%r unexpectedly not greater than %r" % (a, b)) +# +# +# def assert_greater_equal(a, b, msg=None): +# if a >= b: +# success() +# else: +# failure( +# msg or "%r unexpectedly not greater than or equal to %r" +# % (a, b)) +# +# +# def assert_less(a, b, msg=None): +# if a < b: +# success() +# else: +# failure(msg or "%r unexpectedly not less than %r" % (a, b)) +# +# +# def assert_less_equal(a, b, msg=None): +# if a <= b: +# success() +# else: +# failure( +# msg or "%r unexpectedly not less than or equal to %r" % (a, b)) +# +# +# def assert_is_instance(a, b, msg=None): +# if isinstance(a, b): +# success() +# else: +# failure(msg or "got %r, expected %r" % (type(a), b)) +# +# +# def assert_in(a, b, msg=None): +# if a in b: +# success() +# else: +# failure(msg or "%r unexpectedly not in %r" % (a, b)) +# +# +# def assert_match(v, pattern, msg=None): +# import re +# if re.match(pattern, v): +# success() +# else: +# failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) +# +# +# def assert_exception(exc_class, func): +# import sys +# import traceback +# try: +# func() +# except exc_class: +# success() +# except: +# failure("expected %r exception, got %r" % ( +# exc_class.__name__, sys.exc_info()[0].__name__)) +# traceback.print_exc() +# else: +# failure( +# "expected %r exception, got no exception" % exc_class.__name__) +# +# +# def assert_no_exception(func): +# import sys +# import traceback +# try: +# func() +# except: +# failure("expected no exception, got %r" % sys.exc_info()[0].__name__) +# traceback.print_exc() +# else: +# success() +# +# +# def assert_warning(warn_class, func): +# # note: this assert calls func three times! +# import warnings +# +# def warn_error(message, category=UserWarning, **options): +# raise category(message) +# +# def warn_ignore(message, category=UserWarning, **options): +# pass +# warn = warnings.warn +# result = None +# try: +# warnings.warn = warn_ignore +# assert_no_exception(func) +# result = func() +# warnings.warn = warn_error +# assert_exception(warn_class, func) +# finally: +# warnings.warn = warn # restore +# return result +# +# # helpers +# +# from io import BytesIO +# +# +# def fromstring(data): +# from PIL import Image +# return Image.open(BytesIO(data)) +# +# +# def tostring(im, format, **options): +# out = BytesIO() +# im.save(out, format, **options) +# return out.getvalue() + + +def lena(mode="RGB", cache={}): + from PIL import Image + im = cache.get(mode) + if im is None: + if mode == "RGB": + im = Image.open("Images/lena.ppm") + elif mode == "F": + im = lena("L").convert(mode) + elif mode[:4] == "I;16": + im = lena("I").convert(mode) + else: + im = lena("RGB").convert(mode) + cache[mode] = im + return im + + +# def assert_image(im, mode, size, msg=None): +# if mode is not None and im.mode != mode: +# failure(msg or "got mode %r, expected %r" % (im.mode, mode)) +# elif size is not None and im.size != size: +# failure(msg or "got size %r, expected %r" % (im.size, size)) +# else: +# success() +# +# +# def assert_image_equal(a, b, msg=None): +# if a.mode != b.mode: +# failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) +# elif a.size != b.size: +# failure(msg or "got size %r, expected %r" % (a.size, b.size)) +# elif a.tobytes() != b.tobytes(): +# failure(msg or "got different content") +# else: +# success() +# +# +# def assert_image_completely_equal(a, b, msg=None): +# if a != b: +# failure(msg or "images different") +# else: +# success() +# +# +# def assert_image_similar(a, b, epsilon, msg=None): +# epsilon = float(epsilon) +# if a.mode != b.mode: +# return failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) +# elif a.size != b.size: +# return failure(msg or "got size %r, expected %r" % (a.size, b.size)) +# diff = 0 +# try: +# ord(b'0') +# for abyte, bbyte in zip(a.tobytes(), b.tobytes()): +# diff += abs(ord(abyte)-ord(bbyte)) +# except: +# for abyte, bbyte in zip(a.tobytes(), b.tobytes()): +# diff += abs(abyte-bbyte) +# ave_diff = float(diff)/(a.size[0]*a.size[1]) +# if epsilon < ave_diff: +# return failure( +# msg or "average pixel value difference %.4f > epsilon %.4f" % ( +# ave_diff, epsilon)) +# else: +# return success() +# +# +# def tempfile(template, *extra): +# import os +# import os.path +# import sys +# import tempfile +# files = [] +# root = os.path.join(tempfile.gettempdir(), 'pillow-tests') +# try: +# os.mkdir(root) +# except OSError: +# pass +# for temp in (template,) + extra: +# assert temp[:5] in ("temp.", "temp_") +# name = os.path.basename(sys.argv[0]) +# name = temp[:4] + os.path.splitext(name)[0][4:] +# name = name + "_%d" % len(_tempfiles) + temp[4:] +# name = os.path.join(root, name) +# files.append(name) +# _tempfiles.extend(files) +# return files[0] +# +# +# # test runner +# +# def run(): +# global _target, _tests, run +# import sys +# import traceback +# _target = sys.modules["__main__"] +# run = None # no need to run twice +# tests = [] +# for name, value in list(vars(_target).items()): +# if name[:5] == "test_" and type(value) is type(success): +# tests.append((value.__code__.co_firstlineno, name, value)) +# tests.sort() # sort by line +# for lineno, name, func in tests: +# try: +# _tests = [] +# func() +# for func, args in _tests: +# func(*args) +# except: +# t, v, tb = sys.exc_info() +# tb = tb.tb_next +# if tb: +# failure(frame=tb.tb_frame) +# traceback.print_exception(t, v, tb) +# else: +# print("%s:%d: cannot call test function: %s" % ( +# sys.argv[0], lineno, v)) +# failure.count += 1 +# +# +# def yield_test(function, *args): +# # collect delayed/generated tests +# _tests.append((function, args)) +# +# +# def skip(msg=None): +# import os +# print("skip") +# os._exit(0) # don't run exit handlers +# +# +# def ignore(pattern): +# """Tells the driver to ignore messages matching the pattern, for the +# duration of the current test.""" +# print('ignore: %s' % pattern) +# +# +# def _setup(): +# global _logfile +# +# import sys +# if "--coverage" in sys.argv: +# # Temporary: ignore PendingDeprecationWarning from Coverage (Py3.4) +# with warnings.catch_warnings(): +# warnings.simplefilter("ignore") +# import coverage +# cov = coverage.coverage(auto_data=True, include="PIL/*") +# cov.start() +# +# def report(): +# if run: +# run() +# if success.count and not failure.count: +# print("ok") +# # only clean out tempfiles if test passed +# import os +# import os.path +# import tempfile +# for file in _tempfiles: +# try: +# os.remove(file) +# except OSError: +# pass # report? +# temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') +# try: +# os.rmdir(temp_root) +# except OSError: +# pass +# +# import atexit +# atexit.register(report) +# +# if "--log" in sys.argv: +# _logfile = open("test.log", "a") +# +# +# _setup() diff --git a/test/tests.py b/test/tests.py index eb4a8342d..1bd308922 100644 --- a/test/tests.py +++ b/test/tests.py @@ -1,7 +1,7 @@ -import unittest +from tester import unittest -class PillowTests(unittest.TestCase): +class SomeTests(unittest.TestCase): """ Can we start moving the test suite here? """ @@ -10,8 +10,10 @@ class PillowTests(unittest.TestCase): """ Great idea! """ - assert True is True + self.assertTrue(True) if __name__ == '__main__': unittest.main() + +# End of file From 2bde38d8f5a7e4040ddd8d2f5760d927e24a0bd1 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 3 Jun 2014 23:22:02 +0300 Subject: [PATCH 096/168] Move more tests; add more helpers; install unittest2 for Python 2.7 --- .travis.yml | 1 + Tests/test_imagedraw.py | 265 ---------------------------------------- test/test_imagedraw.py | 253 ++++++++++++++++++++++++++++++++++++++ test/tester.py | 15 +++ 4 files changed, 269 insertions(+), 265 deletions(-) delete mode 100644 Tests/test_imagedraw.py create mode 100644 test/test_imagedraw.py diff --git a/.travis.yml b/.travis.yml index 1b864eecf..68e985b9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ install: - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" - "pip install cffi" - "pip install coveralls nose" + - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then pip install unittest2; fi # webp - pushd depends && ./install_webp.sh && popd diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py deleted file mode 100644 index c47638a05..000000000 --- a/Tests/test_imagedraw.py +++ /dev/null @@ -1,265 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageColor -from PIL import ImageDraw - -# Image size -w, h = 100, 100 - -# Bounding box points -x0 = int(w / 4) -x1 = int(x0 * 3) -y0 = int(h / 4) -y1 = int(x0 * 3) - -# Two kinds of bounding box -bbox1 = [(x0, y0), (x1, y1)] -bbox2 = [x0, y0, x1, y1] - -# Two kinds of coordinate sequences -points1 = [(10, 10), (20, 40), (30, 30)] -points2 = [10, 10, 20, 40, 30, 30] - - -def test_sanity(): - - im = lena("RGB").copy() - - draw = ImageDraw.ImageDraw(im) - draw = ImageDraw.Draw(im) - - draw.ellipse(list(range(4))) - draw.line(list(range(10))) - draw.polygon(list(range(100))) - draw.rectangle(list(range(4))) - - success() - - -def test_deprecated(): - - im = lena().copy() - - draw = ImageDraw.Draw(im) - - assert_warning(DeprecationWarning, lambda: draw.setink(0)) - assert_warning(DeprecationWarning, lambda: draw.setfill(0)) - - -def helper_arc(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - # FIXME Fill param should be named outline. - draw.arc(bbox, 0, 180) - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_arc.png")) - - -def test_arc1(): - helper_arc(bbox1) - - -def test_arc2(): - helper_arc(bbox2) - - -def test_bitmap(): - # Arrange - small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.bitmap((10, 10), small) - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png")) - - -def helper_chord(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.chord(bbox, 0, 180, fill="red", outline="yellow") - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_chord.png")) - - -def test_chord1(): - helper_chord(bbox1) - - -def test_chord2(): - helper_chord(bbox2) - - -def helper_ellipse(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.ellipse(bbox, fill="green", outline="blue") - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_ellipse.png")) - - -def test_ellipse1(): - helper_ellipse(bbox1) - - -def test_ellipse2(): - helper_ellipse(bbox2) - - -def helper_line(points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.line(points1, fill="yellow", width=2) - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) - - -def test_line1(): - helper_line(points1) - - -def test_line2(): - helper_line(points2) - - -def helper_pieslice(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.pieslice(bbox, -90, 45, fill="white", outline="blue") - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_pieslice.png")) - - -def test_pieslice1(): - helper_pieslice(bbox1) - - -def test_pieslice2(): - helper_pieslice(bbox2) - - -def helper_point(points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.point(points1, fill="yellow") - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png")) - - -def test_point1(): - helper_point(points1) - - -def test_point2(): - helper_point(points2) - - -def helper_polygon(points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.polygon(points1, fill="red", outline="blue") - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) - - -def test_polygon1(): - helper_polygon(points1) - - -def test_polygon2(): - helper_polygon(points2) - - -def helper_rectangle(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.rectangle(bbox, fill="black", outline="green") - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) - - -def test_rectangle1(): - helper_rectangle(bbox1) - - -def test_rectangle2(): - helper_rectangle(bbox2) - - -def test_floodfill(): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - draw.rectangle(bbox2, outline="yellow", fill="green") - centre_point = (int(w/2), int(h/2)) - - # Act - ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill.png")) - - -def test_floodfill_border(): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - draw.rectangle(bbox2, outline="yellow", fill="green") - centre_point = (int(w/2), int(h/2)) - - # Act - ImageDraw.floodfill( - im, centre_point, ImageColor.getrgb("red"), - border=ImageColor.getrgb("black")) - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) - - -# End of file diff --git a/test/test_imagedraw.py b/test/test_imagedraw.py new file mode 100644 index 000000000..60e9b6f03 --- /dev/null +++ b/test/test_imagedraw.py @@ -0,0 +1,253 @@ +from tester import unittest, PillowTestCase, lena + +from PIL import Image + +from PIL import Image +from PIL import ImageColor +from PIL import ImageDraw + +# Image size +w, h = 100, 100 + +# Bounding box points +x0 = int(w / 4) +x1 = int(x0 * 3) +y0 = int(h / 4) +y1 = int(x0 * 3) + +# Two kinds of bounding box +bbox1 = [(x0, y0), (x1, y1)] +bbox2 = [x0, y0, x1, y1] + +# Two kinds of coordinate sequences +points1 = [(10, 10), (20, 40), (30, 30)] +points2 = [10, 10, 20, 40, 30, 30] + + +class TestImageDraw(PillowTestCase): + + def test_sanity(self): + im = lena("RGB").copy() + + draw = ImageDraw.ImageDraw(im) + draw = ImageDraw.Draw(im) + + draw.ellipse(list(range(4))) + draw.line(list(range(10))) + draw.polygon(list(range(100))) + draw.rectangle(list(range(4))) + + def test_deprecated(self): + im = lena().copy() + + draw = ImageDraw.Draw(im) + + self.assert_warning(DeprecationWarning, lambda: draw.setink(0)) + self.assert_warning(DeprecationWarning, lambda: draw.setfill(0)) + + def helper_arc(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + # FIXME Fill param should be named outline. + draw.arc(bbox, 0, 180) + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_arc.png")) + + def test_arc1(self): + self.helper_arc(bbox1) + + def test_arc2(self): + self.helper_arc(bbox2) + + def test_bitmap(self): + # Arrange + small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.bitmap((10, 10), small) + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_bitmap.png")) + + def helper_chord(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.chord(bbox, 0, 180, fill="red", outline="yellow") + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_chord.png")) + + def test_chord1(self): + self.helper_chord(bbox1) + + def test_chord2(self): + self.helper_chord(bbox2) + + def helper_ellipse(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.ellipse(bbox, fill="green", outline="blue") + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_ellipse.png")) + + def test_ellipse1(self): + self.helper_ellipse(bbox1) + + def test_ellipse2(self): + self.helper_ellipse(bbox2) + + def helper_line(self, points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.line(points1, fill="yellow", width=2) + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_line.png")) + + def test_line1(self): + self.helper_line(points1) + + def test_line2(self): + self.helper_line(points2) + + def helper_pieslice(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.pieslice(bbox, -90, 45, fill="white", outline="blue") + del draw + + # Assert + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_pieslice.png")) + + def test_pieslice1(self): + self.helper_pieslice(bbox1) + + def test_pieslice2(self): + self.helper_pieslice(bbox2) + + def helper_point(self, points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.point(points1, fill="yellow") + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_point.png")) + + def test_point1(self): + self.helper_point(points1) + + + def test_point2(self): + self.helper_point(points2) + + def helper_polygon(self, points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.polygon(points1, fill="red", outline="blue") + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_polygon.png")) + + def test_polygon1(self): + self.helper_polygon(points1) + + + def test_polygon2(self): + self.helper_polygon(points2) + + + def helper_rectangle(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.rectangle(bbox, fill="black", outline="green") + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_rectangle.png")) + + def test_rectangle1(self): + self.helper_rectangle(bbox1) + + def test_rectangle2(self): + self.helper_rectangle(bbox2) + + def test_floodfill(self): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + draw.rectangle(bbox2, outline="yellow", fill="green") + centre_point = (int(w/2), int(h/2)) + + # Act + ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_floodfill.png")) + + def test_floodfill_border(self): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + draw.rectangle(bbox2, outline="yellow", fill="green") + centre_point = (int(w/2), int(h/2)) + + # Act + ImageDraw.floodfill( + im, centre_point, ImageColor.getrgb("red"), + border=ImageColor.getrgb("black")) + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_floodfill2.png")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/tester.py b/test/tester.py index 77b38a15a..894d37883 100644 --- a/test/tester.py +++ b/test/tester.py @@ -23,6 +23,21 @@ class PillowTestCase(unittest.TestCase): a.tobytes(), b.tobytes(), msg or "got different content") + def assert_warning(self, warn_class, func): + import warnings + + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + + # Hopefully trigger a warning. + func() + + # Verify some things. + self.assertEqual(len(w), 1) + assert issubclass(w[-1].category, warn_class) + self.assertIn("deprecated", str(w[-1].message)) + # # require that deprecation warnings are triggered # import warnings From 682ad75759548f0e3cd590fcfb14f396701061e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 3 Jun 2014 16:34:23 -0400 Subject: [PATCH 097/168] DOC: fix name in docstring --- PIL/Image.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 887ceabc1..e064ed9ef 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1529,7 +1529,7 @@ class Image: clockwise around its centre. :param angle: In degrees counter clockwise. - :param filter: An optional resampling filter. This can be + :param resample: An optional resampling filter. This can be one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 environment), or :py:attr:`PIL.Image.BICUBIC` @@ -1550,7 +1550,6 @@ class Image: math.cos(angle), math.sin(angle), 0.0, -math.sin(angle), math.cos(angle), 0.0 ] - def transform(x, y, matrix=matrix): (a, b, c, d, e, f) = matrix From d082c58043423e5eb510151ce394c003a92775e1 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 3 Jun 2014 23:35:21 +0300 Subject: [PATCH 098/168] pep8/pyflakes --- .travis.yml | 2 ++ test/test_imagedraw.py | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 68e985b9a..4a2250771 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,5 +45,7 @@ after_success: - pip install pep8 pyflakes - pep8 PIL/*.py - pyflakes PIL/*.py + - pep8 test/*.py + - pyflakes test/*.py - pep8 Tests/*.py - pyflakes Tests/*.py diff --git a/test/test_imagedraw.py b/test/test_imagedraw.py index 60e9b6f03..f9a5e5f6a 100644 --- a/test/test_imagedraw.py +++ b/test/test_imagedraw.py @@ -1,7 +1,5 @@ from tester import unittest, PillowTestCase, lena -from PIL import Image - from PIL import Image from PIL import ImageColor from PIL import ImageDraw @@ -146,7 +144,8 @@ class TestImageDraw(PillowTestCase): del draw # Assert - self.assert_image_equal(im, Image.open("Tests/images/imagedraw_pieslice.png")) + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_pieslice.png")) def test_pieslice1(self): self.helper_pieslice(bbox1) @@ -170,7 +169,6 @@ class TestImageDraw(PillowTestCase): def test_point1(self): self.helper_point(points1) - def test_point2(self): self.helper_point(points2) @@ -190,11 +188,9 @@ class TestImageDraw(PillowTestCase): def test_polygon1(self): self.helper_polygon(points1) - def test_polygon2(self): self.helper_polygon(points2) - def helper_rectangle(self, bbox): # Arrange im = Image.new("RGB", (w, h)) From 9497525abcedc7664b5de82f1fad2b8e1294bbad Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 3 Jun 2014 16:39:17 -0400 Subject: [PATCH 099/168] Fix coveralls.io URLs --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 33b9813d0..0831a6b81 100644 --- a/README.rst +++ b/README.rst @@ -17,7 +17,7 @@ Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Pyt :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads -.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master - :target: https://coveralls.io/r/python-imaging/Pillow?branch=master +.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master + :target: https://coveralls.io/r/python-pillow/Pillow?branch=master The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more. From 2e52f3ab46d98b7978f3d145c20dc38fbd84d90f Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 4 Jun 2014 00:05:48 +0300 Subject: [PATCH 100/168] Move more tests; remove some redundant helpers --- Tests/test_000_sanity.py | 24 --------- Tests/test_image_histogram.py | 19 ------- Tests/test_imageenhance.py | 19 ------- Tests/test_lib_image.py | 30 ----------- test/test_000_sanity.py | 32 ++++++++++++ test/test_image_histogram.py | 26 ++++++++++ test/test_imageenhance.py | 28 +++++++++++ test/test_lib_image.py | 39 +++++++++++++++ test/tester.py | 94 ----------------------------------- 9 files changed, 125 insertions(+), 186 deletions(-) delete mode 100644 Tests/test_000_sanity.py delete mode 100644 Tests/test_image_histogram.py delete mode 100644 Tests/test_imageenhance.py delete mode 100644 Tests/test_lib_image.py create mode 100644 test/test_000_sanity.py create mode 100644 test/test_image_histogram.py create mode 100644 test/test_imageenhance.py create mode 100644 test/test_lib_image.py diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py deleted file mode 100644 index a30786458..000000000 --- a/Tests/test_000_sanity.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import print_function -from tester import * - -import PIL -import PIL.Image - -# Make sure we have the binary extension -im = PIL.Image.core.new("L", (100, 100)) - -assert PIL.Image.VERSION[:3] == '1.1' - -# Create an image and do stuff with it. -im = PIL.Image.new("1", (100, 100)) -assert (im.mode, im.size) == ('1', (100, 100)) -assert len(im.tobytes()) == 1300 - -# Create images in all remaining major modes. -im = PIL.Image.new("L", (100, 100)) -im = PIL.Image.new("P", (100, 100)) -im = PIL.Image.new("RGB", (100, 100)) -im = PIL.Image.new("I", (100, 100)) -im = PIL.Image.new("F", (100, 100)) - -print("ok") diff --git a/Tests/test_image_histogram.py b/Tests/test_image_histogram.py deleted file mode 100644 index c86cb578a..000000000 --- a/Tests/test_image_histogram.py +++ /dev/null @@ -1,19 +0,0 @@ -from tester import * - -from PIL import Image - -def test_histogram(): - - def histogram(mode): - h = lena(mode).histogram() - return len(h), min(h), max(h) - - assert_equal(histogram("1"), (256, 0, 8872)) - assert_equal(histogram("L"), (256, 0, 199)) - assert_equal(histogram("I"), (256, 0, 199)) - assert_equal(histogram("F"), (256, 0, 199)) - assert_equal(histogram("P"), (256, 0, 2912)) - assert_equal(histogram("RGB"), (768, 0, 285)) - assert_equal(histogram("RGBA"), (1024, 0, 16384)) - assert_equal(histogram("CMYK"), (1024, 0, 16384)) - assert_equal(histogram("YCbCr"), (768, 0, 741)) diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py deleted file mode 100644 index 04f16bfa5..000000000 --- a/Tests/test_imageenhance.py +++ /dev/null @@ -1,19 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageEnhance - -def test_sanity(): - - # FIXME: assert_image - assert_no_exception(lambda: ImageEnhance.Color(lena()).enhance(0.5)) - assert_no_exception(lambda: ImageEnhance.Contrast(lena()).enhance(0.5)) - assert_no_exception(lambda: ImageEnhance.Brightness(lena()).enhance(0.5)) - assert_no_exception(lambda: ImageEnhance.Sharpness(lena()).enhance(0.5)) - -def test_crash(): - - # crashes on small images - im = Image.new("RGB", (1, 1)) - assert_no_exception(lambda: ImageEnhance.Sharpness(im).enhance(0.5)) - diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py deleted file mode 100644 index 93aa694d8..000000000 --- a/Tests/test_lib_image.py +++ /dev/null @@ -1,30 +0,0 @@ -from tester import * - -from PIL import Image - -def test_setmode(): - - im = Image.new("L", (1, 1), 255) - im.im.setmode("1") - assert_equal(im.im.getpixel((0, 0)), 255) - im.im.setmode("L") - assert_equal(im.im.getpixel((0, 0)), 255) - - im = Image.new("1", (1, 1), 1) - im.im.setmode("L") - assert_equal(im.im.getpixel((0, 0)), 255) - im.im.setmode("1") - assert_equal(im.im.getpixel((0, 0)), 255) - - im = Image.new("RGB", (1, 1), (1, 2, 3)) - im.im.setmode("RGB") - assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) - im.im.setmode("RGBA") - assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) - im.im.setmode("RGBX") - assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) - im.im.setmode("RGB") - assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) - - assert_exception(ValueError, lambda: im.im.setmode("L")) - assert_exception(ValueError, lambda: im.im.setmode("RGBABCDE")) diff --git a/test/test_000_sanity.py b/test/test_000_sanity.py new file mode 100644 index 000000000..b536dd96a --- /dev/null +++ b/test/test_000_sanity.py @@ -0,0 +1,32 @@ +from tester import unittest, PillowTestCase + +import PIL +import PIL.Image + + +class TestSanity(PillowTestCase): + + def test_sanity(self): + + # Make sure we have the binary extension + im = PIL.Image.core.new("L", (100, 100)) + + self.assertEqual(PIL.Image.VERSION[:3], '1.1') + + # Create an image and do stuff with it. + im = PIL.Image.new("1", (100, 100)) + self.assertEqual((im.mode, im.size), ('1', (100, 100))) + self.assertEqual(len(im.tobytes()), 1300) + + # Create images in all remaining major modes. + im = PIL.Image.new("L", (100, 100)) + im = PIL.Image.new("P", (100, 100)) + im = PIL.Image.new("RGB", (100, 100)) + im = PIL.Image.new("I", (100, 100)) + im = PIL.Image.new("F", (100, 100)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_histogram.py b/test/test_image_histogram.py new file mode 100644 index 000000000..8a46149ff --- /dev/null +++ b/test/test_image_histogram.py @@ -0,0 +1,26 @@ +from tester import unittest, PillowTestCase, lena + + +class TestImageHistogram(PillowTestCase): + + def test_histogram(self): + + def histogram(mode): + h = lena(mode).histogram() + return len(h), min(h), max(h) + + self.assertEqual(histogram("1"), (256, 0, 8872)) + self.assertEqual(histogram("L"), (256, 0, 199)) + self.assertEqual(histogram("I"), (256, 0, 199)) + self.assertEqual(histogram("F"), (256, 0, 199)) + self.assertEqual(histogram("P"), (256, 0, 2912)) + self.assertEqual(histogram("RGB"), (768, 0, 285)) + self.assertEqual(histogram("RGBA"), (1024, 0, 16384)) + self.assertEqual(histogram("CMYK"), (1024, 0, 16384)) + self.assertEqual(histogram("YCbCr"), (768, 0, 741)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imageenhance.py b/test/test_imageenhance.py new file mode 100644 index 000000000..c126bf344 --- /dev/null +++ b/test/test_imageenhance.py @@ -0,0 +1,28 @@ +from tester import unittest, PillowTestCase, lena + +from PIL import Image +from PIL import ImageEnhance + + +class TestImageEnhance(PillowTestCase): + + def test_sanity(self): + + # FIXME: assert_image + # Implicit asserts no exception: + ImageEnhance.Color(lena()).enhance(0.5) + ImageEnhance.Contrast(lena()).enhance(0.5) + ImageEnhance.Brightness(lena()).enhance(0.5) + ImageEnhance.Sharpness(lena()).enhance(0.5) + + def test_crash(self): + + # crashes on small images + im = Image.new("RGB", (1, 1)) + ImageEnhance.Sharpness(im).enhance(0.5) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_lib_image.py b/test/test_lib_image.py new file mode 100644 index 000000000..8694c1d51 --- /dev/null +++ b/test/test_lib_image.py @@ -0,0 +1,39 @@ +from tester import unittest, PillowTestCase + +from PIL import Image + + +class TestSanity(PillowTestCase): + + def test_setmode(self): + + im = Image.new("L", (1, 1), 255) + im.im.setmode("1") + self.assertEqual(im.im.getpixel((0, 0)), 255) + im.im.setmode("L") + self.assertEqual(im.im.getpixel((0, 0)), 255) + + im = Image.new("1", (1, 1), 1) + im.im.setmode("L") + self.assertEqual(im.im.getpixel((0, 0)), 255) + im.im.setmode("1") + self.assertEqual(im.im.getpixel((0, 0)), 255) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.im.setmode("RGB") + self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3)) + im.im.setmode("RGBA") + self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGBX") + self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGB") + self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3)) + + self.assertRaises(ValueError, lambda: im.im.setmode("L")) + self.assertRaises(ValueError, lambda: im.im.setmode("RGBABCDE")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/tester.py b/test/tester.py index 894d37883..8df32e317 100644 --- a/test/tester.py +++ b/test/tester.py @@ -112,51 +112,6 @@ py3 = (sys.version_info >= (3, 0)) # assert_equal(a, b, msg) # # -# def assert_greater(a, b, msg=None): -# if a > b: -# success() -# else: -# failure(msg or "%r unexpectedly not greater than %r" % (a, b)) -# -# -# def assert_greater_equal(a, b, msg=None): -# if a >= b: -# success() -# else: -# failure( -# msg or "%r unexpectedly not greater than or equal to %r" -# % (a, b)) -# -# -# def assert_less(a, b, msg=None): -# if a < b: -# success() -# else: -# failure(msg or "%r unexpectedly not less than %r" % (a, b)) -# -# -# def assert_less_equal(a, b, msg=None): -# if a <= b: -# success() -# else: -# failure( -# msg or "%r unexpectedly not less than or equal to %r" % (a, b)) -# -# -# def assert_is_instance(a, b, msg=None): -# if isinstance(a, b): -# success() -# else: -# failure(msg or "got %r, expected %r" % (type(a), b)) -# -# -# def assert_in(a, b, msg=None): -# if a in b: -# success() -# else: -# failure(msg or "%r unexpectedly not in %r" % (a, b)) -# -# # def assert_match(v, pattern, msg=None): # import re # if re.match(pattern, v): @@ -165,55 +120,6 @@ py3 = (sys.version_info >= (3, 0)) # failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) # # -# def assert_exception(exc_class, func): -# import sys -# import traceback -# try: -# func() -# except exc_class: -# success() -# except: -# failure("expected %r exception, got %r" % ( -# exc_class.__name__, sys.exc_info()[0].__name__)) -# traceback.print_exc() -# else: -# failure( -# "expected %r exception, got no exception" % exc_class.__name__) -# -# -# def assert_no_exception(func): -# import sys -# import traceback -# try: -# func() -# except: -# failure("expected no exception, got %r" % sys.exc_info()[0].__name__) -# traceback.print_exc() -# else: -# success() -# -# -# def assert_warning(warn_class, func): -# # note: this assert calls func three times! -# import warnings -# -# def warn_error(message, category=UserWarning, **options): -# raise category(message) -# -# def warn_ignore(message, category=UserWarning, **options): -# pass -# warn = warnings.warn -# result = None -# try: -# warnings.warn = warn_ignore -# assert_no_exception(func) -# result = func() -# warnings.warn = warn_error -# assert_exception(warn_class, func) -# finally: -# warnings.warn = warn # restore -# return result -# # # helpers # # from io import BytesIO From e518879c0ea37be3afe64ac4d8817e4267b551a6 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 4 Jun 2014 00:11:31 +0300 Subject: [PATCH 101/168] Fix Coveralls link --- docs/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 2eb845bff..a8c204228 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,8 +16,8 @@ Python Imaging Library by Fredrik Lundh and Contributors. :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads -.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master - :target: https://coveralls.io/r/python-imaging/Pillow?branch=master +.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master + :target: https://coveralls.io/r/python-pillow/Pillow?branch=master :alt: Test coverage To start using Pillow, please read the :doc:`installation From c8626c6e93d1d05c1d25e649447b4a3d3b62ee8e Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 4 Jun 2014 09:09:23 +0300 Subject: [PATCH 102/168] Move more tests --- Tests/test_image_array.py | 33 -------------- Tests/test_image_crop.py | 52 --------------------- Tests/test_image_filter.py | 82 --------------------------------- Tests/test_image_putdata.py | 40 ---------------- Tests/test_imagefilter.py | 31 ------------- Tests/test_imagestat.py | 52 --------------------- test/test_image_array.py | 46 +++++++++++++++++++ test/test_image_crop.py | 59 ++++++++++++++++++++++++ test/test_image_filter.py | 91 +++++++++++++++++++++++++++++++++++++ test/test_image_putdata.py | 48 +++++++++++++++++++ test/test_imagefilter.py | 37 +++++++++++++++ test/test_imagestat.py | 63 +++++++++++++++++++++++++ test/tester.py | 11 ----- test/tests.py | 19 -------- 14 files changed, 344 insertions(+), 320 deletions(-) delete mode 100644 Tests/test_image_array.py delete mode 100644 Tests/test_image_crop.py delete mode 100644 Tests/test_image_filter.py delete mode 100644 Tests/test_image_putdata.py delete mode 100644 Tests/test_imagefilter.py delete mode 100644 Tests/test_imagestat.py create mode 100644 test/test_image_array.py create mode 100644 test/test_image_crop.py create mode 100644 test/test_image_filter.py create mode 100644 test/test_image_putdata.py create mode 100644 test/test_imagefilter.py create mode 100644 test/test_imagestat.py delete mode 100644 test/tests.py diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py deleted file mode 100644 index 351621d3a..000000000 --- a/Tests/test_image_array.py +++ /dev/null @@ -1,33 +0,0 @@ -from tester import * - -from PIL import Image - -im = lena().resize((128, 100)) - -def test_toarray(): - def test(mode): - ai = im.convert(mode).__array_interface__ - return ai["shape"], ai["typestr"], len(ai["data"]) - # assert_equal(test("1"), ((100, 128), '|b1', 1600)) - assert_equal(test("L"), ((100, 128), '|u1', 12800)) - assert_equal(test("I"), ((100, 128), Image._ENDIAN + 'i4', 51200)) # FIXME: wrong? - assert_equal(test("F"), ((100, 128), Image._ENDIAN + 'f4', 51200)) # FIXME: wrong? - assert_equal(test("RGB"), ((100, 128, 3), '|u1', 38400)) - assert_equal(test("RGBA"), ((100, 128, 4), '|u1', 51200)) - assert_equal(test("RGBX"), ((100, 128, 4), '|u1', 51200)) - -def test_fromarray(): - def test(mode): - i = im.convert(mode) - a = i.__array_interface__ - a["strides"] = 1 # pretend it's non-contigous - i.__array_interface__ = a # patch in new version of attribute - out = Image.fromarray(i) - return out.mode, out.size, list(i.getdata()) == list(out.getdata()) - # assert_equal(test("1"), ("1", (128, 100), True)) - assert_equal(test("L"), ("L", (128, 100), True)) - assert_equal(test("I"), ("I", (128, 100), True)) - assert_equal(test("F"), ("F", (128, 100), True)) - assert_equal(test("RGB"), ("RGB", (128, 100), True)) - assert_equal(test("RGBA"), ("RGBA", (128, 100), True)) - assert_equal(test("RGBX"), ("RGBA", (128, 100), True)) diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py deleted file mode 100644 index 973606309..000000000 --- a/Tests/test_image_crop.py +++ /dev/null @@ -1,52 +0,0 @@ -from tester import * - -from PIL import Image - -def test_crop(): - def crop(mode): - out = lena(mode).crop((50, 50, 100, 100)) - assert_equal(out.mode, mode) - assert_equal(out.size, (50, 50)) - for mode in "1", "P", "L", "RGB", "I", "F": - yield_test(crop, mode) - -def test_wide_crop(): - - def crop(*bbox): - i = im.crop(bbox) - h = i.histogram() - while h and not h[-1]: - del h[-1] - return tuple(h) - - im = Image.new("L", (100, 100), 1) - - assert_equal(crop(0, 0, 100, 100), (0, 10000)) - assert_equal(crop(25, 25, 75, 75), (0, 2500)) - - # sides - assert_equal(crop(-25, 0, 25, 50), (1250, 1250)) - assert_equal(crop(0, -25, 50, 25), (1250, 1250)) - assert_equal(crop(75, 0, 125, 50), (1250, 1250)) - assert_equal(crop(0, 75, 50, 125), (1250, 1250)) - - assert_equal(crop(-25, 25, 125, 75), (2500, 5000)) - assert_equal(crop(25, -25, 75, 125), (2500, 5000)) - - # corners - assert_equal(crop(-25, -25, 25, 25), (1875, 625)) - assert_equal(crop(75, -25, 125, 25), (1875, 625)) - assert_equal(crop(75, 75, 125, 125), (1875, 625)) - assert_equal(crop(-25, 75, 25, 125), (1875, 625)) - -# -------------------------------------------------------------------- - -def test_negative_crop(): - # Check negative crop size (@PIL171) - - im = Image.new("L", (512, 512)) - im = im.crop((400, 400, 200, 200)) - - assert_equal(im.size, (0, 0)) - assert_equal(len(im.getdata()), 0) - assert_exception(IndexError, lambda: im.getdata()[0]) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py deleted file mode 100644 index f61e0c954..000000000 --- a/Tests/test_image_filter.py +++ /dev/null @@ -1,82 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageFilter - -def test_sanity(): - - def filter(filter): - im = lena("L") - out = im.filter(filter) - assert_equal(out.mode, im.mode) - assert_equal(out.size, im.size) - - filter(ImageFilter.BLUR) - filter(ImageFilter.CONTOUR) - filter(ImageFilter.DETAIL) - filter(ImageFilter.EDGE_ENHANCE) - filter(ImageFilter.EDGE_ENHANCE_MORE) - filter(ImageFilter.EMBOSS) - filter(ImageFilter.FIND_EDGES) - filter(ImageFilter.SMOOTH) - filter(ImageFilter.SMOOTH_MORE) - filter(ImageFilter.SHARPEN) - filter(ImageFilter.MaxFilter) - filter(ImageFilter.MedianFilter) - filter(ImageFilter.MinFilter) - filter(ImageFilter.ModeFilter) - filter(ImageFilter.Kernel((3, 3), list(range(9)))) - - assert_exception(TypeError, lambda: filter("hello")) - -def test_crash(): - - # crashes on small images - im = Image.new("RGB", (1, 1)) - assert_no_exception(lambda: im.filter(ImageFilter.SMOOTH)) - - im = Image.new("RGB", (2, 2)) - assert_no_exception(lambda: im.filter(ImageFilter.SMOOTH)) - - im = Image.new("RGB", (3, 3)) - assert_no_exception(lambda: im.filter(ImageFilter.SMOOTH)) - -def test_modefilter(): - - def modefilter(mode): - im = Image.new(mode, (3, 3), None) - im.putdata(list(range(9))) - # image is: - # 0 1 2 - # 3 4 5 - # 6 7 8 - mod = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) - im.putdata([0, 0, 1, 2, 5, 1, 5, 2, 0]) # mode=0 - mod2 = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) - return mod, mod2 - - assert_equal(modefilter("1"), (4, 0)) - assert_equal(modefilter("L"), (4, 0)) - assert_equal(modefilter("P"), (4, 0)) - assert_equal(modefilter("RGB"), ((4, 0, 0), (0, 0, 0))) - -def test_rankfilter(): - - def rankfilter(mode): - im = Image.new(mode, (3, 3), None) - im.putdata(list(range(9))) - # image is: - # 0 1 2 - # 3 4 5 - # 6 7 8 - min = im.filter(ImageFilter.MinFilter).getpixel((1, 1)) - med = im.filter(ImageFilter.MedianFilter).getpixel((1, 1)) - max = im.filter(ImageFilter.MaxFilter).getpixel((1, 1)) - return min, med, max - - assert_equal(rankfilter("1"), (0, 4, 8)) - assert_equal(rankfilter("L"), (0, 4, 8)) - assert_exception(ValueError, lambda: rankfilter("P")) - assert_equal(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0))) - assert_equal(rankfilter("I"), (0, 4, 8)) - assert_equal(rankfilter("F"), (0.0, 4.0, 8.0)) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py deleted file mode 100644 index e25359fdf..000000000 --- a/Tests/test_image_putdata.py +++ /dev/null @@ -1,40 +0,0 @@ -from tester import * - -import sys - -from PIL import Image - -def test_sanity(): - - im1 = lena() - - data = list(im1.getdata()) - - im2 = Image.new(im1.mode, im1.size, 0) - im2.putdata(data) - - assert_image_equal(im1, im2) - - # readonly - im2 = Image.new(im1.mode, im2.size, 0) - im2.readonly = 1 - im2.putdata(data) - - assert_false(im2.readonly) - assert_image_equal(im1, im2) - - -def test_long_integers(): - # see bug-200802-systemerror - def put(value): - im = Image.new("RGBA", (1, 1)) - im.putdata([value]) - return im.getpixel((0, 0)) - assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255)) - assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255)) - assert_equal(put(-1), (255, 255, 255, 255)) - assert_equal(put(-1), (255, 255, 255, 255)) - if sys.maxsize > 2**32: - assert_equal(put(sys.maxsize), (255, 255, 255, 255)) - else: - assert_equal(put(sys.maxsize), (255, 255, 255, 127)) diff --git a/Tests/test_imagefilter.py b/Tests/test_imagefilter.py deleted file mode 100644 index 214f88024..000000000 --- a/Tests/test_imagefilter.py +++ /dev/null @@ -1,31 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageFilter - -def test_sanity(): - # see test_image_filter for more tests - - assert_no_exception(lambda: ImageFilter.MaxFilter) - assert_no_exception(lambda: ImageFilter.MedianFilter) - assert_no_exception(lambda: ImageFilter.MinFilter) - assert_no_exception(lambda: ImageFilter.ModeFilter) - assert_no_exception(lambda: ImageFilter.Kernel((3, 3), list(range(9)))) - assert_no_exception(lambda: ImageFilter.GaussianBlur) - assert_no_exception(lambda: ImageFilter.GaussianBlur(5)) - assert_no_exception(lambda: ImageFilter.UnsharpMask) - assert_no_exception(lambda: ImageFilter.UnsharpMask(10)) - - assert_no_exception(lambda: ImageFilter.BLUR) - assert_no_exception(lambda: ImageFilter.CONTOUR) - assert_no_exception(lambda: ImageFilter.DETAIL) - assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE) - assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE_MORE) - assert_no_exception(lambda: ImageFilter.EMBOSS) - assert_no_exception(lambda: ImageFilter.FIND_EDGES) - assert_no_exception(lambda: ImageFilter.SMOOTH) - assert_no_exception(lambda: ImageFilter.SMOOTH_MORE) - assert_no_exception(lambda: ImageFilter.SHARPEN) - - - diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py deleted file mode 100644 index 02a461e22..000000000 --- a/Tests/test_imagestat.py +++ /dev/null @@ -1,52 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageStat - -def test_sanity(): - - im = lena() - - st = ImageStat.Stat(im) - st = ImageStat.Stat(im.histogram()) - st = ImageStat.Stat(im, Image.new("1", im.size, 1)) - - assert_no_exception(lambda: st.extrema) - assert_no_exception(lambda: st.sum) - assert_no_exception(lambda: st.mean) - assert_no_exception(lambda: st.median) - assert_no_exception(lambda: st.rms) - assert_no_exception(lambda: st.sum2) - assert_no_exception(lambda: st.var) - assert_no_exception(lambda: st.stddev) - assert_exception(AttributeError, lambda: st.spam) - - assert_exception(TypeError, lambda: ImageStat.Stat(1)) - -def test_lena(): - - im = lena() - - st = ImageStat.Stat(im) - - # verify a few values - assert_equal(st.extrema[0], (61, 255)) - assert_equal(st.median[0], 197) - assert_equal(st.sum[0], 2954416) - assert_equal(st.sum[1], 2027250) - assert_equal(st.sum[2], 1727331) - -def test_constant(): - - im = Image.new("L", (128, 128), 128) - - st = ImageStat.Stat(im) - - assert_equal(st.extrema[0], (128, 128)) - assert_equal(st.sum[0], 128**3) - assert_equal(st.sum2[0], 128**4) - assert_equal(st.mean[0], 128) - assert_equal(st.median[0], 128) - assert_equal(st.rms[0], 128) - assert_equal(st.var[0], 0) - assert_equal(st.stddev[0], 0) diff --git a/test/test_image_array.py b/test/test_image_array.py new file mode 100644 index 000000000..97599f9f0 --- /dev/null +++ b/test/test_image_array.py @@ -0,0 +1,46 @@ +from tester import unittest, PillowTestCase, lena + +from PIL import Image + +im = lena().resize((128, 100)) + + +class TestImageCrop(PillowTestCase): + + def test_toarray(self): + def test(mode): + ai = im.convert(mode).__array_interface__ + return ai["shape"], ai["typestr"], len(ai["data"]) + # self.assertEqual(test("1"), ((100, 128), '|b1', 1600)) + self.assertEqual(test("L"), ((100, 128), '|u1', 12800)) + + # FIXME: wrong? + self.assertEqual(test("I"), ((100, 128), Image._ENDIAN + 'i4', 51200)) + # FIXME: wrong? + self.assertEqual(test("F"), ((100, 128), Image._ENDIAN + 'f4', 51200)) + + self.assertEqual(test("RGB"), ((100, 128, 3), '|u1', 38400)) + self.assertEqual(test("RGBA"), ((100, 128, 4), '|u1', 51200)) + self.assertEqual(test("RGBX"), ((100, 128, 4), '|u1', 51200)) + + def test_fromarray(self): + def test(mode): + i = im.convert(mode) + a = i.__array_interface__ + a["strides"] = 1 # pretend it's non-contigous + i.__array_interface__ = a # patch in new version of attribute + out = Image.fromarray(i) + return out.mode, out.size, list(i.getdata()) == list(out.getdata()) + # self.assertEqual(test("1"), ("1", (128, 100), True)) + self.assertEqual(test("L"), ("L", (128, 100), True)) + self.assertEqual(test("I"), ("I", (128, 100), True)) + self.assertEqual(test("F"), ("F", (128, 100), True)) + self.assertEqual(test("RGB"), ("RGB", (128, 100), True)) + self.assertEqual(test("RGBA"), ("RGBA", (128, 100), True)) + self.assertEqual(test("RGBX"), ("RGBA", (128, 100), True)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_crop.py b/test/test_image_crop.py new file mode 100644 index 000000000..a81ae000a --- /dev/null +++ b/test/test_image_crop.py @@ -0,0 +1,59 @@ +from tester import unittest, PillowTestCase, lena + +from PIL import Image + + +class TestImageCrop(PillowTestCase): + + def test_crop(self): + def crop(mode): + out = lena(mode).crop((50, 50, 100, 100)) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, (50, 50)) + for mode in "1", "P", "L", "RGB", "I", "F": + crop(mode) + + def test_wide_crop(self): + + def crop(*bbox): + i = im.crop(bbox) + h = i.histogram() + while h and not h[-1]: + del h[-1] + return tuple(h) + + im = Image.new("L", (100, 100), 1) + + self.assertEqual(crop(0, 0, 100, 100), (0, 10000)) + self.assertEqual(crop(25, 25, 75, 75), (0, 2500)) + + # sides + self.assertEqual(crop(-25, 0, 25, 50), (1250, 1250)) + self.assertEqual(crop(0, -25, 50, 25), (1250, 1250)) + self.assertEqual(crop(75, 0, 125, 50), (1250, 1250)) + self.assertEqual(crop(0, 75, 50, 125), (1250, 1250)) + + self.assertEqual(crop(-25, 25, 125, 75), (2500, 5000)) + self.assertEqual(crop(25, -25, 75, 125), (2500, 5000)) + + # corners + self.assertEqual(crop(-25, -25, 25, 25), (1875, 625)) + self.assertEqual(crop(75, -25, 125, 25), (1875, 625)) + self.assertEqual(crop(75, 75, 125, 125), (1875, 625)) + self.assertEqual(crop(-25, 75, 25, 125), (1875, 625)) + + def test_negative_crop(self): + # Check negative crop size (@PIL171) + + im = Image.new("L", (512, 512)) + im = im.crop((400, 400, 200, 200)) + + self.assertEqual(im.size, (0, 0)) + self.assertEqual(len(im.getdata()), 0) + self.assertRaises(IndexError, lambda: im.getdata()[0]) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_filter.py b/test/test_image_filter.py new file mode 100644 index 000000000..02fa18c18 --- /dev/null +++ b/test/test_image_filter.py @@ -0,0 +1,91 @@ +from tester import unittest, PillowTestCase, lena + +from PIL import Image +from PIL import ImageFilter + + +class TestImageFilter(PillowTestCase): + + def test_sanity(self): + + def filter(filter): + im = lena("L") + out = im.filter(filter) + self.assertEqual(out.mode, im.mode) + self.assertEqual(out.size, im.size) + + filter(ImageFilter.BLUR) + filter(ImageFilter.CONTOUR) + filter(ImageFilter.DETAIL) + filter(ImageFilter.EDGE_ENHANCE) + filter(ImageFilter.EDGE_ENHANCE_MORE) + filter(ImageFilter.EMBOSS) + filter(ImageFilter.FIND_EDGES) + filter(ImageFilter.SMOOTH) + filter(ImageFilter.SMOOTH_MORE) + filter(ImageFilter.SHARPEN) + filter(ImageFilter.MaxFilter) + filter(ImageFilter.MedianFilter) + filter(ImageFilter.MinFilter) + filter(ImageFilter.ModeFilter) + filter(ImageFilter.Kernel((3, 3), list(range(9)))) + + self.assertRaises(TypeError, lambda: filter("hello")) + + def test_crash(self): + + # crashes on small images + im = Image.new("RGB", (1, 1)) + im.filter(ImageFilter.SMOOTH) + + im = Image.new("RGB", (2, 2)) + im.filter(ImageFilter.SMOOTH) + + im = Image.new("RGB", (3, 3)) + im.filter(ImageFilter.SMOOTH) + + def test_modefilter(self): + + def modefilter(mode): + im = Image.new(mode, (3, 3), None) + im.putdata(list(range(9))) + # image is: + # 0 1 2 + # 3 4 5 + # 6 7 8 + mod = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) + im.putdata([0, 0, 1, 2, 5, 1, 5, 2, 0]) # mode=0 + mod2 = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) + return mod, mod2 + + self.assertEqual(modefilter("1"), (4, 0)) + self.assertEqual(modefilter("L"), (4, 0)) + self.assertEqual(modefilter("P"), (4, 0)) + self.assertEqual(modefilter("RGB"), ((4, 0, 0), (0, 0, 0))) + + def test_rankfilter(self): + + def rankfilter(mode): + im = Image.new(mode, (3, 3), None) + im.putdata(list(range(9))) + # image is: + # 0 1 2 + # 3 4 5 + # 6 7 8 + min = im.filter(ImageFilter.MinFilter).getpixel((1, 1)) + med = im.filter(ImageFilter.MedianFilter).getpixel((1, 1)) + max = im.filter(ImageFilter.MaxFilter).getpixel((1, 1)) + return min, med, max + + self.assertEqual(rankfilter("1"), (0, 4, 8)) + self.assertEqual(rankfilter("L"), (0, 4, 8)) + self.assertRaises(ValueError, lambda: rankfilter("P")) + self.assertEqual(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0))) + self.assertEqual(rankfilter("I"), (0, 4, 8)) + self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_putdata.py b/test/test_image_putdata.py new file mode 100644 index 000000000..16e938bf8 --- /dev/null +++ b/test/test_image_putdata.py @@ -0,0 +1,48 @@ +from tester import unittest, PillowTestCase, lena + +import sys + +from PIL import Image + + +class TestImagePutData(PillowTestCase): + + def test_sanity(self): + + im1 = lena() + + data = list(im1.getdata()) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.putdata(data) + + self.assert_image_equal(im1, im2) + + # readonly + im2 = Image.new(im1.mode, im2.size, 0) + im2.readonly = 1 + im2.putdata(data) + + self.assertFalse(im2.readonly) + self.assert_image_equal(im1, im2) + + def test_long_integers(self): + # see bug-200802-systemerror + def put(value): + im = Image.new("RGBA", (1, 1)) + im.putdata([value]) + return im.getpixel((0, 0)) + self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) + self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) + self.assertEqual(put(-1), (255, 255, 255, 255)) + self.assertEqual(put(-1), (255, 255, 255, 255)) + if sys.maxsize > 2**32: + self.assertEqual(put(sys.maxsize), (255, 255, 255, 255)) + else: + self.assertEqual(put(sys.maxsize), (255, 255, 255, 127)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagefilter.py b/test/test_imagefilter.py new file mode 100644 index 000000000..1f01cb615 --- /dev/null +++ b/test/test_imagefilter.py @@ -0,0 +1,37 @@ +from tester import unittest, PillowTestCase + +from PIL import ImageFilter + + +class TestImageFilter(PillowTestCase): + + def test_sanity(self): + # see test_image_filter for more tests + + # Check these run. Exceptions cause failures. + ImageFilter.MaxFilter + ImageFilter.MedianFilter + ImageFilter.MinFilter + ImageFilter.ModeFilter + ImageFilter.Kernel((3, 3), list(range(9))) + ImageFilter.GaussianBlur + ImageFilter.GaussianBlur(5) + ImageFilter.UnsharpMask + ImageFilter.UnsharpMask(10) + + ImageFilter.BLUR + ImageFilter.CONTOUR + ImageFilter.DETAIL + ImageFilter.EDGE_ENHANCE + ImageFilter.EDGE_ENHANCE_MORE + ImageFilter.EMBOSS + ImageFilter.FIND_EDGES + ImageFilter.SMOOTH + ImageFilter.SMOOTH_MORE + ImageFilter.SHARPEN + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagestat.py b/test/test_imagestat.py new file mode 100644 index 000000000..fc228a886 --- /dev/null +++ b/test/test_imagestat.py @@ -0,0 +1,63 @@ +from tester import unittest, PillowTestCase, lena + +from PIL import Image +from PIL import ImageStat + + +class TestImageStat(PillowTestCase): + + def test_sanity(self): + + im = lena() + + st = ImageStat.Stat(im) + st = ImageStat.Stat(im.histogram()) + st = ImageStat.Stat(im, Image.new("1", im.size, 1)) + + # Check these run. Exceptions will cause failures. + st.extrema + st.sum + st.mean + st.median + st.rms + st.sum2 + st.var + st.stddev + + self.assertRaises(AttributeError, lambda: st.spam) + + self.assertRaises(TypeError, lambda: ImageStat.Stat(1)) + + def test_lena(self): + + im = lena() + + st = ImageStat.Stat(im) + + # verify a few values + self.assertEqual(st.extrema[0], (61, 255)) + self.assertEqual(st.median[0], 197) + self.assertEqual(st.sum[0], 2954416) + self.assertEqual(st.sum[1], 2027250) + self.assertEqual(st.sum[2], 1727331) + + def test_constant(self): + + im = Image.new("L", (128, 128), 128) + + st = ImageStat.Stat(im) + + self.assertEqual(st.extrema[0], (128, 128)) + self.assertEqual(st.sum[0], 128**3) + self.assertEqual(st.sum2[0], 128**4) + self.assertEqual(st.mean[0], 128) + self.assertEqual(st.median[0], 128) + self.assertEqual(st.rms[0], 128) + self.assertEqual(st.var[0], 0) + self.assertEqual(st.stddev[0], 0) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/tester.py b/test/tester.py index 8df32e317..418f1261f 100644 --- a/test/tester.py +++ b/test/tester.py @@ -161,17 +161,6 @@ def lena(mode="RGB", cache={}): # success() # # -# def assert_image_equal(a, b, msg=None): -# if a.mode != b.mode: -# failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) -# elif a.size != b.size: -# failure(msg or "got size %r, expected %r" % (a.size, b.size)) -# elif a.tobytes() != b.tobytes(): -# failure(msg or "got different content") -# else: -# success() -# -# # def assert_image_completely_equal(a, b, msg=None): # if a != b: # failure(msg or "images different") diff --git a/test/tests.py b/test/tests.py deleted file mode 100644 index 1bd308922..000000000 --- a/test/tests.py +++ /dev/null @@ -1,19 +0,0 @@ -from tester import unittest - - -class SomeTests(unittest.TestCase): - """ - Can we start moving the test suite here? - """ - - def test_suite_should_move_here(self): - """ - Great idea! - """ - self.assertTrue(True) - - -if __name__ == '__main__': - unittest.main() - -# End of file From f14b928ce68436ede7f4fcc083179dd89bddc1de Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 4 Jun 2014 10:11:22 +0300 Subject: [PATCH 103/168] Rename tester.py to helper.py because it no longer drives the tests --- test/{tester.py => helper.py} | 0 test/test_000_sanity.py | 2 +- test/test_image_array.py | 2 +- test/test_image_crop.py | 2 +- test/test_image_filter.py | 2 +- test/test_image_frombytes.py | 2 +- test/test_image_getim.py | 2 +- test/test_image_histogram.py | 2 +- test/test_image_putdata.py | 2 +- test/test_image_tobytes.py | 2 +- test/test_imagedraw.py | 2 +- test/test_imageenhance.py | 2 +- test/test_imagefilter.py | 2 +- test/test_imagestat.py | 2 +- test/test_lib_image.py | 2 +- 15 files changed, 14 insertions(+), 14 deletions(-) rename test/{tester.py => helper.py} (100%) diff --git a/test/tester.py b/test/helper.py similarity index 100% rename from test/tester.py rename to test/helper.py diff --git a/test/test_000_sanity.py b/test/test_000_sanity.py index b536dd96a..22e582ec3 100644 --- a/test/test_000_sanity.py +++ b/test/test_000_sanity.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase +from helper import unittest, PillowTestCase import PIL import PIL.Image diff --git a/test/test_image_array.py b/test/test_image_array.py index 97599f9f0..a0f5f29e1 100644 --- a/test/test_image_array.py +++ b/test/test_image_array.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_crop.py b/test/test_image_crop.py index a81ae000a..da93fe7c8 100644 --- a/test/test_image_crop.py +++ b/test/test_image_crop.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_filter.py b/test/test_image_filter.py index 02fa18c18..4a85b0a2e 100644 --- a/test/test_image_filter.py +++ b/test/test_image_filter.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageFilter diff --git a/test/test_image_frombytes.py b/test/test_image_frombytes.py index ee68b6722..aad8046a1 100644 --- a/test/test_image_frombytes.py +++ b/test/test_image_frombytes.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_getim.py b/test/test_image_getim.py index 02744a529..d498d3923 100644 --- a/test/test_image_getim.py +++ b/test/test_image_getim.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena, py3 +from helper import unittest, PillowTestCase, lena, py3 class TestImageGetIm(PillowTestCase): diff --git a/test/test_image_histogram.py b/test/test_image_histogram.py index 8a46149ff..70f78a1fb 100644 --- a/test/test_image_histogram.py +++ b/test/test_image_histogram.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImageHistogram(PillowTestCase): diff --git a/test/test_image_putdata.py b/test/test_image_putdata.py index 16e938bf8..c7c3115aa 100644 --- a/test/test_image_putdata.py +++ b/test/test_image_putdata.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena import sys diff --git a/test/test_image_tobytes.py b/test/test_image_tobytes.py index ee7b4c9e6..3be9128c1 100644 --- a/test/test_image_tobytes.py +++ b/test/test_image_tobytes.py @@ -1,4 +1,4 @@ -from tester import unittest, lena +from helper import unittest, lena class TestImageToBytes(unittest.TestCase): diff --git a/test/test_imagedraw.py b/test/test_imagedraw.py index f9a5e5f6a..44403e50c 100644 --- a/test/test_imagedraw.py +++ b/test/test_imagedraw.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageColor diff --git a/test/test_imageenhance.py b/test/test_imageenhance.py index c126bf344..433c49cf6 100644 --- a/test/test_imageenhance.py +++ b/test/test_imageenhance.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageEnhance diff --git a/test/test_imagefilter.py b/test/test_imagefilter.py index 1f01cb615..f7edb409a 100644 --- a/test/test_imagefilter.py +++ b/test/test_imagefilter.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import ImageFilter diff --git a/test/test_imagestat.py b/test/test_imagestat.py index fc228a886..4d30ff023 100644 --- a/test/test_imagestat.py +++ b/test/test_imagestat.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageStat diff --git a/test/test_lib_image.py b/test/test_lib_image.py index 8694c1d51..e0a903b00 100644 --- a/test/test_lib_image.py +++ b/test/test_lib_image.py @@ -1,4 +1,4 @@ -from tester import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image From 184c380e106a48aac92f3fd58b70c8b3a82b6d28 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 4 Jun 2014 16:18:04 +0300 Subject: [PATCH 104/168] Convert more tests, including a method to skip on ImportError --- Tests/test_image_copy.py | 12 ------------ Tests/test_imagegrab.py | 13 ------------- test/helper.py | 20 +++++++++++--------- test/test_image_copy.py | 20 ++++++++++++++++++++ test/test_imagegrab.py | 25 +++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 34 deletions(-) delete mode 100644 Tests/test_image_copy.py delete mode 100644 Tests/test_imagegrab.py create mode 100644 test/test_image_copy.py create mode 100644 test/test_imagegrab.py diff --git a/Tests/test_image_copy.py b/Tests/test_image_copy.py deleted file mode 100644 index 40a3dc496..000000000 --- a/Tests/test_image_copy.py +++ /dev/null @@ -1,12 +0,0 @@ -from tester import * - -from PIL import Image - -def test_copy(): - def copy(mode): - im = lena(mode) - out = im.copy() - assert_equal(out.mode, mode) - assert_equal(out.size, im.size) - for mode in "1", "P", "L", "RGB", "I", "F": - yield_test(copy, mode) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py deleted file mode 100644 index 67ff71960..000000000 --- a/Tests/test_imagegrab.py +++ /dev/null @@ -1,13 +0,0 @@ -from tester import * - -from PIL import Image -try: - from PIL import ImageGrab -except ImportError as v: - skip(v) - -def test_grab(): - im = ImageGrab.grab() - assert_image(im, im.mode, im.size) - - diff --git a/test/helper.py b/test/helper.py index 418f1261f..8c45858b6 100644 --- a/test/helper.py +++ b/test/helper.py @@ -12,6 +12,17 @@ else: class PillowTestCase(unittest.TestCase): + def assert_image(self, im, mode, size, msg=None): + if mode is not None: + self.assertEqual( + im.mode, mode, + msg or "got mode %r, expected %r" % (im.mode, mode)) + + if size is not None: + self.assertEqual( + im.size, size, + msg or "got size %r, expected %r" % (im.size, size)) + def assert_image_equal(self, a, b, msg=None): self.assertEqual( a.mode, b.mode, @@ -152,15 +163,6 @@ def lena(mode="RGB", cache={}): return im -# def assert_image(im, mode, size, msg=None): -# if mode is not None and im.mode != mode: -# failure(msg or "got mode %r, expected %r" % (im.mode, mode)) -# elif size is not None and im.size != size: -# failure(msg or "got size %r, expected %r" % (im.size, size)) -# else: -# success() -# -# # def assert_image_completely_equal(a, b, msg=None): # if a != b: # failure(msg or "images different") diff --git a/test/test_image_copy.py b/test/test_image_copy.py new file mode 100644 index 000000000..a7882db94 --- /dev/null +++ b/test/test_image_copy.py @@ -0,0 +1,20 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + + +class TestImageCopy(PillowTestCase): + + def test_copy(self): + def copy(mode): + im = lena(mode) + out = im.copy() + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) + for mode in "1", "P", "L", "RGB", "I", "F": + copy(mode) + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagegrab.py b/test/test_imagegrab.py new file mode 100644 index 000000000..fb953f435 --- /dev/null +++ b/test/test_imagegrab.py @@ -0,0 +1,25 @@ +from helper import unittest, PillowTestCase + +try: + from PIL import ImageGrab + + class TestImageCopy(PillowTestCase): + + def test_grab(self): + im = ImageGrab.grab() + self.assert_image(im, im.mode, im.size) + + def test_grab2(self): + im = ImageGrab.grab() + self.assert_image(im, im.mode, im.size) + +except ImportError as v: + class TestImageCopy(PillowTestCase): + def test_skip(self): + self.skipTest(v) + + +if __name__ == '__main__': + unittest.main() + +# End of file From b7eb1de922791f1604aac91c032de5cf4e3e2490 Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 4 Jun 2014 17:09:59 +0300 Subject: [PATCH 105/168] Fix skipping --- test/test_imagegrab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_imagegrab.py b/test/test_imagegrab.py index fb953f435..2275d34a1 100644 --- a/test/test_imagegrab.py +++ b/test/test_imagegrab.py @@ -13,10 +13,10 @@ try: im = ImageGrab.grab() self.assert_image(im, im.mode, im.size) -except ImportError as v: +except ImportError: class TestImageCopy(PillowTestCase): def test_skip(self): - self.skipTest(v) + self.skipTest("ImportError") if __name__ == '__main__': From 6858aef5b15823855f24b3f47e3d3a307ca9841a Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 4 Jun 2014 18:32:17 -0400 Subject: [PATCH 106/168] Update --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index fbe2d3b0c..71a42e315 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,10 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ + +- ImageCms fixes + [hugovk] + - Added more ImageDraw tests [hugovk] From d385dd81a7c3a425e0052f81bbbaa5e2ebb5a575 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 11:39:29 +0300 Subject: [PATCH 107/168] Converttest_image_load.py --- Tests/test_image_load.py | 27 --------------------------- test/test_image_load.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 27 deletions(-) delete mode 100644 Tests/test_image_load.py create mode 100644 test/test_image_load.py diff --git a/Tests/test_image_load.py b/Tests/test_image_load.py deleted file mode 100644 index b385b9686..000000000 --- a/Tests/test_image_load.py +++ /dev/null @@ -1,27 +0,0 @@ -from tester import * - -from PIL import Image - -import os - -def test_sanity(): - - im = lena() - - pix = im.load() - - assert_equal(pix[0, 0], (223, 162, 133)) - -def test_close(): - im = Image.open("Images/lena.gif") - assert_no_exception(lambda: im.close()) - assert_exception(ValueError, lambda: im.load()) - assert_exception(ValueError, lambda: im.getpixel((0,0))) - -def test_contextmanager(): - fn = None - with Image.open("Images/lena.gif") as im: - fn = im.fp.fileno() - assert_no_exception(lambda: os.fstat(fn)) - - assert_exception(OSError, lambda: os.fstat(fn)) diff --git a/test/test_image_load.py b/test/test_image_load.py new file mode 100644 index 000000000..80f505e6c --- /dev/null +++ b/test/test_image_load.py @@ -0,0 +1,35 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + +import os + + +class TestImageLoad(PillowTestCase): + + def test_sanity(self): + + im = lena() + + pix = im.load() + + self.assertEqual(pix[0, 0], (223, 162, 133)) + + def test_close(self): + im = Image.open("Images/lena.gif") + im.close() + self.assert_exception(ValueError, lambda: im.load()) + self.assert_exception(ValueError, lambda: im.getpixel((0, 0))) + + def test_contextmanager(self): + fn = None + with Image.open("Images/lena.gif") as im: + fn = im.fp.fileno() + os.fstat(fn) + + self.assert_exception(OSError, lambda: os.fstat(fn)) + +if __name__ == '__main__': + unittest.main() + +# End of file From 133120c6a655e933186f6da64a2b5548f6431d97 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 11:45:01 +0300 Subject: [PATCH 108/168] assert_exception is assertRaises --- .travis.yml | 4 ++-- test/test_image_load.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4a2250771..7e4e5e9c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,13 +30,13 @@ script: # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then nosetests test/; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi # Cover the others - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* -m nose test/; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi after_success: diff --git a/test/test_image_load.py b/test/test_image_load.py index 80f505e6c..2001c233a 100644 --- a/test/test_image_load.py +++ b/test/test_image_load.py @@ -18,8 +18,8 @@ class TestImageLoad(PillowTestCase): def test_close(self): im = Image.open("Images/lena.gif") im.close() - self.assert_exception(ValueError, lambda: im.load()) - self.assert_exception(ValueError, lambda: im.getpixel((0, 0))) + self.assertRaises(ValueError, lambda: im.load()) + self.assertRaises(ValueError, lambda: im.getpixel((0, 0))) def test_contextmanager(self): fn = None @@ -27,7 +27,7 @@ class TestImageLoad(PillowTestCase): fn = im.fp.fileno() os.fstat(fn) - self.assert_exception(OSError, lambda: os.fstat(fn)) + self.assertRaises(OSError, lambda: os.fstat(fn)) if __name__ == '__main__': unittest.main() From 82df06243c1aa5d2c5d7f75262e83dfad7620f54 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 12:26:54 +0300 Subject: [PATCH 109/168] Convert more tests --- test/test_image_tobitmap.py | 24 ++++++++++++ test/test_imagechops.py | 74 +++++++++++++++++++++++++++++++++++ test/test_imagemath.py | 78 +++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 test/test_image_tobitmap.py create mode 100644 test/test_imagechops.py create mode 100644 test/test_imagemath.py diff --git a/test/test_image_tobitmap.py b/test/test_image_tobitmap.py new file mode 100644 index 000000000..00e5af0cd --- /dev/null +++ b/test/test_image_tobitmap.py @@ -0,0 +1,24 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + + +class TestImage(PillowTestCase): + +def test_sanity(self): + + self.assertRaises(ValueError, lambda: lena().tobitmap()) + assert_no_exception(lambda: lena().convert("1").tobitmap()) + + im1 = lena().convert("1") + + bitmap = im1.tobitmap() + + assert_true(isinstance(bitmap, bytes)) + assert_image_equal(im1, fromstring(bitmap)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagechops.py b/test/test_imagechops.py new file mode 100644 index 000000000..ec162d52f --- /dev/null +++ b/test/test_imagechops.py @@ -0,0 +1,74 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image +from PIL import ImageChops + + +class TestImage(PillowTestCase): + + def test_sanity(self): + + im = lena("L") + + ImageChops.constant(im, 128) + ImageChops.duplicate(im) + ImageChops.invert(im) + ImageChops.lighter(im, im) + ImageChops.darker(im, im) + ImageChops.difference(im, im) + ImageChops.multiply(im, im) + ImageChops.screen(im, im) + + ImageChops.add(im, im) + ImageChops.add(im, im, 2.0) + ImageChops.add(im, im, 2.0, 128) + ImageChops.subtract(im, im) + ImageChops.subtract(im, im, 2.0) + ImageChops.subtract(im, im, 2.0, 128) + + ImageChops.add_modulo(im, im) + ImageChops.subtract_modulo(im, im) + + ImageChops.blend(im, im, 0.5) + ImageChops.composite(im, im, im) + + ImageChops.offset(im, 10) + ImageChops.offset(im, 10, 20) + + def test_logical(self): + + def table(op, a, b): + out = [] + for x in (a, b): + imx = Image.new("1", (1, 1), x) + for y in (a, b): + imy = Image.new("1", (1, 1), y) + out.append(op(imx, imy).getpixel((0, 0))) + return tuple(out) + + self.assertEqual( + table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) + self.assertEqual( + table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) + self.assertEqual( + table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) + + self.assertEqual( + table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) + self.assertEqual( + table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) + self.assertEqual( + table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + + self.assertEqual( + table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) + self.assertEqual( + table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) + self.assertEqual( + table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagemath.py b/test/test_imagemath.py new file mode 100644 index 000000000..17d43d25a --- /dev/null +++ b/test/test_imagemath.py @@ -0,0 +1,78 @@ +from helper import unittest, PillowTestCase + +from PIL import Image +from PIL import ImageMath + + +def pixel(im): + if hasattr(im, "im"): + return "%s %r" % (im.mode, im.getpixel((0, 0))) + else: + if isinstance(im, type(0)): + return int(im) # hack to deal with booleans + print(im) + +A = Image.new("L", (1, 1), 1) +B = Image.new("L", (1, 1), 2) +F = Image.new("F", (1, 1), 3) +I = Image.new("I", (1, 1), 4) + +images = {"A": A, "B": B, "F": F, "I": I} + + +class TestImageMath(PillowTestCase): + + def test_sanity(self): + self.assertEqual(ImageMath.eval("1"), 1) + self.assertEqual(ImageMath.eval("1+A", A=2), 3) + self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") + self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") + self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + self.assertEqual(pixel( + ImageMath.eval("int(float(A)+B)", images)), "I 3") + + def test_ops(self): + + self.assertEqual(pixel(ImageMath.eval("-A", images)), "I -1") + self.assertEqual(pixel(ImageMath.eval("+B", images)), "L 2") + + self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") + self.assertEqual(pixel(ImageMath.eval("A-B", images)), "I -1") + self.assertEqual(pixel(ImageMath.eval("A*B", images)), "I 2") + self.assertEqual(pixel(ImageMath.eval("A/B", images)), "I 0") + self.assertEqual(pixel(ImageMath.eval("B**2", images)), "I 4") + self.assertEqual(pixel( + ImageMath.eval("B**33", images)), "I 2147483647") + + self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + self.assertEqual(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") + self.assertEqual(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") + self.assertEqual(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") + self.assertEqual(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") + self.assertEqual(pixel( + ImageMath.eval("float(B)**33", images)), "F 8589934592.0") + + def test_logical(self): + self.assertEqual(pixel(ImageMath.eval("not A", images)), 0) + self.assertEqual(pixel(ImageMath.eval("A and B", images)), "L 2") + self.assertEqual(pixel(ImageMath.eval("A or B", images)), "L 1") + + def test_convert(self): + self.assertEqual(pixel( + ImageMath.eval("convert(A+B, 'L')", images)), "L 3") + self.assertEqual(pixel( + ImageMath.eval("convert(A+B, '1')", images)), "1 0") + self.assertEqual(pixel( + ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") + + def test_compare(self): + self.assertEqual(pixel(ImageMath.eval("min(A, B)", images)), "I 1") + self.assertEqual(pixel(ImageMath.eval("max(A, B)", images)), "I 2") + self.assertEqual(pixel(ImageMath.eval("A == 1", images)), "I 1") + self.assertEqual(pixel(ImageMath.eval("A == 2", images)), "I 0") + + +if __name__ == '__main__': + unittest.main() + +# End of file From 1f94ea1fc613bc0fb1489235f84b673d8ca06d4a Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 12:30:59 +0300 Subject: [PATCH 110/168] Merge --- Tests/test_image_tobitmap.py | 15 --------- Tests/test_imagechops.py | 56 -------------------------------- Tests/test_imagemath.py | 62 ------------------------------------ 3 files changed, 133 deletions(-) delete mode 100644 Tests/test_image_tobitmap.py delete mode 100644 Tests/test_imagechops.py delete mode 100644 Tests/test_imagemath.py diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py deleted file mode 100644 index 6fb10dd53..000000000 --- a/Tests/test_image_tobitmap.py +++ /dev/null @@ -1,15 +0,0 @@ -from tester import * - -from PIL import Image - -def test_sanity(): - - assert_exception(ValueError, lambda: lena().tobitmap()) - assert_no_exception(lambda: lena().convert("1").tobitmap()) - - im1 = lena().convert("1") - - bitmap = im1.tobitmap() - - assert_true(isinstance(bitmap, bytes)) - assert_image_equal(im1, fromstring(bitmap)) diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py deleted file mode 100644 index 16eaaf55e..000000000 --- a/Tests/test_imagechops.py +++ /dev/null @@ -1,56 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageChops - -def test_sanity(): - - im = lena("L") - - ImageChops.constant(im, 128) - ImageChops.duplicate(im) - ImageChops.invert(im) - ImageChops.lighter(im, im) - ImageChops.darker(im, im) - ImageChops.difference(im, im) - ImageChops.multiply(im, im) - ImageChops.screen(im, im) - - ImageChops.add(im, im) - ImageChops.add(im, im, 2.0) - ImageChops.add(im, im, 2.0, 128) - ImageChops.subtract(im, im) - ImageChops.subtract(im, im, 2.0) - ImageChops.subtract(im, im, 2.0, 128) - - ImageChops.add_modulo(im, im) - ImageChops.subtract_modulo(im, im) - - ImageChops.blend(im, im, 0.5) - ImageChops.composite(im, im, im) - - ImageChops.offset(im, 10) - ImageChops.offset(im, 10, 20) - -def test_logical(): - - def table(op, a, b): - out = [] - for x in (a, b): - imx = Image.new("1", (1, 1), x) - for y in (a, b): - imy = Image.new("1", (1, 1), y) - out.append(op(imx, imy).getpixel((0, 0))) - return tuple(out) - - assert_equal(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) - assert_equal(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) - assert_equal(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) - - assert_equal(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) - assert_equal(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) - assert_equal(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) - - assert_equal(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) - assert_equal(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) - assert_equal(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py deleted file mode 100644 index eaeb711ba..000000000 --- a/Tests/test_imagemath.py +++ /dev/null @@ -1,62 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageMath - -def pixel(im): - if hasattr(im, "im"): - return "%s %r" % (im.mode, im.getpixel((0, 0))) - else: - if isinstance(im, type(0)): - return int(im) # hack to deal with booleans - print(im) - -A = Image.new("L", (1, 1), 1) -B = Image.new("L", (1, 1), 2) -F = Image.new("F", (1, 1), 3) -I = Image.new("I", (1, 1), 4) - -images = {"A": A, "B": B, "F": F, "I": I} - -def test_sanity(): - assert_equal(ImageMath.eval("1"), 1) - assert_equal(ImageMath.eval("1+A", A=2), 3) - assert_equal(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") - assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") - assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") - assert_equal(pixel(ImageMath.eval("int(float(A)+B)", images)), "I 3") - -def test_ops(): - - assert_equal(pixel(ImageMath.eval("-A", images)), "I -1") - assert_equal(pixel(ImageMath.eval("+B", images)), "L 2") - - assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") - assert_equal(pixel(ImageMath.eval("A-B", images)), "I -1") - assert_equal(pixel(ImageMath.eval("A*B", images)), "I 2") - assert_equal(pixel(ImageMath.eval("A/B", images)), "I 0") - assert_equal(pixel(ImageMath.eval("B**2", images)), "I 4") - assert_equal(pixel(ImageMath.eval("B**33", images)), "I 2147483647") - - assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") - assert_equal(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") - assert_equal(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") - assert_equal(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") - assert_equal(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") - assert_equal(pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0") - -def test_logical(): - assert_equal(pixel(ImageMath.eval("not A", images)), 0) - assert_equal(pixel(ImageMath.eval("A and B", images)), "L 2") - assert_equal(pixel(ImageMath.eval("A or B", images)), "L 1") - -def test_convert(): - assert_equal(pixel(ImageMath.eval("convert(A+B, 'L')", images)), "L 3") - assert_equal(pixel(ImageMath.eval("convert(A+B, '1')", images)), "1 0") - assert_equal(pixel(ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") - -def test_compare(): - assert_equal(pixel(ImageMath.eval("min(A, B)", images)), "I 1") - assert_equal(pixel(ImageMath.eval("max(A, B)", images)), "I 2") - assert_equal(pixel(ImageMath.eval("A == 1", images)), "I 1") - assert_equal(pixel(ImageMath.eval("A == 2", images)), "I 0") From 34db40ddce99e6c4daf9b64873689d1249cb7a0f Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 12:39:15 +0300 Subject: [PATCH 111/168] Convert test_image_tobitmap.py --- test/helper.py | 31 +++++++++++++++---------------- test/test_image_tobitmap.py | 18 ++++++++---------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/test/helper.py b/test/helper.py index 8c45858b6..0958c56a9 100644 --- a/test/helper.py +++ b/test/helper.py @@ -129,22 +129,21 @@ py3 = (sys.version_info >= (3, 0)) # success() # else: # failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) -# -# -# # helpers -# -# from io import BytesIO -# -# -# def fromstring(data): -# from PIL import Image -# return Image.open(BytesIO(data)) -# -# -# def tostring(im, format, **options): -# out = BytesIO() -# im.save(out, format, **options) -# return out.getvalue() + + +# helpers + +def fromstring(data): + from io import BytesIO + from PIL import Image + return Image.open(BytesIO(data)) + + +def tostring(im, format, **options): + from io import BytesIO + out = BytesIO() + im.save(out, format, **options) + return out.getvalue() def lena(mode="RGB", cache={}): diff --git a/test/test_image_tobitmap.py b/test/test_image_tobitmap.py index 00e5af0cd..4e2a16df0 100644 --- a/test/test_image_tobitmap.py +++ b/test/test_image_tobitmap.py @@ -1,21 +1,19 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image +from helper import unittest, PillowTestCase, lena, fromstring class TestImage(PillowTestCase): -def test_sanity(self): + def test_sanity(self): - self.assertRaises(ValueError, lambda: lena().tobitmap()) - assert_no_exception(lambda: lena().convert("1").tobitmap()) + self.assertRaises(ValueError, lambda: lena().tobitmap()) + lena().convert("1").tobitmap() - im1 = lena().convert("1") + im1 = lena().convert("1") - bitmap = im1.tobitmap() + bitmap = im1.tobitmap() - assert_true(isinstance(bitmap, bytes)) - assert_image_equal(im1, fromstring(bitmap)) + self.assertIsInstance(bitmap, bytes) + self.assert_image_equal(im1, fromstring(bitmap)) if __name__ == '__main__': From 3fda42d280f13928b7647d97252331dd768b5bd7 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 13:39:45 +0300 Subject: [PATCH 112/168] Convert more tests --- Tests/test_file_fli.py | 14 ---- Tests/test_image_getbbox.py | 36 --------- Tests/test_image_getpixel.py | 49 ------------ Tests/test_imagefont.py | 135 -------------------------------- test/helper.py | 46 +++++------ test/test_file_fli.py | 23 ++++++ test/test_image_getbbox.py | 45 +++++++++++ test/test_image_getpixel.py | 54 +++++++++++++ test/test_imagefont.py | 145 +++++++++++++++++++++++++++++++++++ 9 files changed, 290 insertions(+), 257 deletions(-) delete mode 100644 Tests/test_file_fli.py delete mode 100644 Tests/test_image_getbbox.py delete mode 100644 Tests/test_image_getpixel.py delete mode 100644 Tests/test_imagefont.py create mode 100644 test/test_file_fli.py create mode 100644 test/test_image_getbbox.py create mode 100644 test/test_image_getpixel.py create mode 100644 test/test_imagefont.py diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py deleted file mode 100644 index 4e06a732e..000000000 --- a/Tests/test_file_fli.py +++ /dev/null @@ -1,14 +0,0 @@ -from tester import * - -from PIL import Image - -# sample ppm stream -file = "Images/lena.fli" -data = open(file, "rb").read() - -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "P") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "FLI") diff --git a/Tests/test_image_getbbox.py b/Tests/test_image_getbbox.py deleted file mode 100644 index c0f846169..000000000 --- a/Tests/test_image_getbbox.py +++ /dev/null @@ -1,36 +0,0 @@ -from tester import * - -from PIL import Image - -def test_sanity(): - - bbox = lena().getbbox() - assert_true(isinstance(bbox, tuple)) - -def test_bbox(): - - # 8-bit mode - im = Image.new("L", (100, 100), 0) - assert_equal(im.getbbox(), None) - - im.paste(255, (10, 25, 90, 75)) - assert_equal(im.getbbox(), (10, 25, 90, 75)) - - im.paste(255, (25, 10, 75, 90)) - assert_equal(im.getbbox(), (10, 10, 90, 90)) - - im.paste(255, (-10, -10, 110, 110)) - assert_equal(im.getbbox(), (0, 0, 100, 100)) - - # 32-bit mode - im = Image.new("RGB", (100, 100), 0) - assert_equal(im.getbbox(), None) - - im.paste(255, (10, 25, 90, 75)) - assert_equal(im.getbbox(), (10, 25, 90, 75)) - - im.paste(255, (25, 10, 75, 90)) - assert_equal(im.getbbox(), (10, 10, 90, 90)) - - im.paste(255, (-10, -10, 110, 110)) - assert_equal(im.getbbox(), (0, 0, 100, 100)) diff --git a/Tests/test_image_getpixel.py b/Tests/test_image_getpixel.py deleted file mode 100644 index da3d8115e..000000000 --- a/Tests/test_image_getpixel.py +++ /dev/null @@ -1,49 +0,0 @@ -from tester import * - -from PIL import Image - -Image.USE_CFFI_ACCESS=False - -def color(mode): - bands = Image.getmodebands(mode) - if bands == 1: - return 1 - else: - return tuple(range(1, bands+1)) - - - -def check(mode, c=None): - if not c: - c = color(mode) - - #check putpixel - im = Image.new(mode, (1, 1), None) - im.putpixel((0, 0), c) - assert_equal(im.getpixel((0, 0)), c, - "put/getpixel roundtrip failed for mode %s, color %s" % - (mode, c)) - - # check inital color - im = Image.new(mode, (1, 1), c) - assert_equal(im.getpixel((0, 0)), c, - "initial color failed for mode %s, color %s " % - (mode, color)) - -def test_basic(): - for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F", - "P", "PA", "RGB", "RGBA", "RGBX", "CMYK","YCbCr"): - check(mode) - -def test_signedness(): - # see https://github.com/python-pillow/Pillow/issues/452 - # pixelaccess is using signed int* instead of uint* - for mode in ("I;16", "I;16B"): - check(mode, 2**15-1) - check(mode, 2**15) - check(mode, 2**15+1) - check(mode, 2**16-1) - - - - diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py deleted file mode 100644 index 9ac2cdd89..000000000 --- a/Tests/test_imagefont.py +++ /dev/null @@ -1,135 +0,0 @@ -from tester import * - -from PIL import Image -from io import BytesIO -import os - -try: - from PIL import ImageFont - ImageFont.core.getfont # check if freetype is available -except ImportError: - skip() - -from PIL import ImageDraw - -font_path = "Tests/fonts/FreeMono.ttf" -font_size=20 - -def test_sanity(): - assert_match(ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") - -def test_font_with_name(): - assert_no_exception(lambda: ImageFont.truetype(font_path, font_size)) - assert_no_exception(lambda: _render(font_path)) - _clean() - -def _font_as_bytes(): - with open(font_path, 'rb') as f: - font_bytes = BytesIO(f.read()) - return font_bytes - -def test_font_with_filelike(): - assert_no_exception(lambda: ImageFont.truetype(_font_as_bytes(), font_size)) - assert_no_exception(lambda: _render(_font_as_bytes())) - # Usage note: making two fonts from the same buffer fails. - #shared_bytes = _font_as_bytes() - #assert_no_exception(lambda: _render(shared_bytes)) - #assert_exception(Exception, lambda: _render(shared_bytes)) - _clean() - -def test_font_with_open_file(): - with open(font_path, 'rb') as f: - assert_no_exception(lambda: _render(f)) - _clean() - -def test_font_old_parameters(): - assert_warning(DeprecationWarning, lambda: ImageFont.truetype(filename=font_path, size=font_size)) - -def _render(font): - txt = "Hello World!" - ttf = ImageFont.truetype(font, font_size) - w, h = ttf.getsize(txt) - img = Image.new("RGB", (256, 64), "white") - d = ImageDraw.Draw(img) - d.text((10, 10), txt, font=ttf, fill='black') - - img.save('font.png') - return img - -def _clean(): - os.unlink('font.png') - -def test_render_equal(): - img_path = _render(font_path) - with open(font_path, 'rb') as f: - font_filelike = BytesIO(f.read()) - img_filelike = _render(font_filelike) - - assert_image_equal(img_path, img_filelike) - _clean() - - -def test_render_multiline(): - im = Image.new(mode='RGB', size=(300,100)) - draw = ImageDraw.Draw(im) - ttf = ImageFont.truetype(font_path, font_size) - line_spacing = draw.textsize('A', font=ttf)[1] + 8 - lines = ['hey you', 'you are awesome', 'this looks awkward'] - y = 0 - for line in lines: - draw.text((0, y), line, font=ttf) - y += line_spacing - - - target = 'Tests/images/multiline_text.png' - target_img = Image.open(target) - - # some versions of freetype have different horizontal spacing. - # setting a tight epsilon, I'm showing the original test failure - # at epsilon = ~38. - assert_image_similar(im, target_img,.5) - - -def test_rotated_transposed_font(): - img_grey = Image.new("L", (100, 100)) - draw = ImageDraw.Draw(img_grey) - word = "testing" - font = ImageFont.truetype(font_path, font_size) - - orientation = Image.ROTATE_90 - transposed_font = ImageFont.TransposedFont(font, orientation=orientation) - - # Original font - draw.setfont(font) - box_size_a = draw.textsize(word) - - # Rotated font - draw.setfont(transposed_font) - box_size_b = draw.textsize(word) - - # Check (w,h) of box a is (h,w) of box b - assert_equal(box_size_a[0], box_size_b[1]) - assert_equal(box_size_a[1], box_size_b[0]) - - -def test_unrotated_transposed_font(): - img_grey = Image.new("L", (100, 100)) - draw = ImageDraw.Draw(img_grey) - word = "testing" - font = ImageFont.truetype(font_path, font_size) - - orientation = None - transposed_font = ImageFont.TransposedFont(font, orientation=orientation) - - # Original font - draw.setfont(font) - box_size_a = draw.textsize(word) - - # Rotated font - draw.setfont(transposed_font) - box_size_b = draw.textsize(word) - - # Check boxes a and b are same size - assert_equal(box_size_a, box_size_b) - - diff --git a/test/helper.py b/test/helper.py index 0958c56a9..e6684b845 100644 --- a/test/helper.py +++ b/test/helper.py @@ -34,6 +34,29 @@ class PillowTestCase(unittest.TestCase): a.tobytes(), b.tobytes(), msg or "got different content") + def assert_image_similar(self, a, b, epsilon, msg=None): + epsilon = float(epsilon) + self.assertEqual( + a.mode, b.mode, + msg or "got mode %r, expected %r" % (a.mode, b.mode)) + self.assertEqual( + a.size, b.size, + msg or "got size %r, expected %r" % (a.size, b.size)) + + diff = 0 + try: + ord(b'0') + for abyte, bbyte in zip(a.tobytes(), b.tobytes()): + diff += abs(ord(abyte)-ord(bbyte)) + except: + for abyte, bbyte in zip(a.tobytes(), b.tobytes()): + diff += abs(abyte-bbyte) + ave_diff = float(diff)/(a.size[0]*a.size[1]) + self.assertGreaterEqual( + epsilon, ave_diff, + msg or "average pixel value difference %.4f > epsilon %.4f" % ( + ave_diff, epsilon)) + def assert_warning(self, warn_class, func): import warnings @@ -169,29 +192,6 @@ def lena(mode="RGB", cache={}): # success() # # -# def assert_image_similar(a, b, epsilon, msg=None): -# epsilon = float(epsilon) -# if a.mode != b.mode: -# return failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) -# elif a.size != b.size: -# return failure(msg or "got size %r, expected %r" % (a.size, b.size)) -# diff = 0 -# try: -# ord(b'0') -# for abyte, bbyte in zip(a.tobytes(), b.tobytes()): -# diff += abs(ord(abyte)-ord(bbyte)) -# except: -# for abyte, bbyte in zip(a.tobytes(), b.tobytes()): -# diff += abs(abyte-bbyte) -# ave_diff = float(diff)/(a.size[0]*a.size[1]) -# if epsilon < ave_diff: -# return failure( -# msg or "average pixel value difference %.4f > epsilon %.4f" % ( -# ave_diff, epsilon)) -# else: -# return success() -# -# # def tempfile(template, *extra): # import os # import os.path diff --git a/test/test_file_fli.py b/test/test_file_fli.py new file mode 100644 index 000000000..dd22a58f9 --- /dev/null +++ b/test/test_file_fli.py @@ -0,0 +1,23 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + +# sample ppm stream +file = "Images/lena.fli" +data = open(file, "rb").read() + + +class TestImage(PillowTestCase): + + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "P") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "FLI") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_getbbox.py b/test/test_image_getbbox.py new file mode 100644 index 000000000..83a6a3dec --- /dev/null +++ b/test/test_image_getbbox.py @@ -0,0 +1,45 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + + +class TestImage(PillowTestCase): + + def test_sanity(self): + + bbox = lena().getbbox() + self.assertIsInstance(bbox, tuple) + + def test_bbox(self): + + # 8-bit mode + im = Image.new("L", (100, 100), 0) + self.assertEqual(im.getbbox(), None) + + im.paste(255, (10, 25, 90, 75)) + self.assertEqual(im.getbbox(), (10, 25, 90, 75)) + + im.paste(255, (25, 10, 75, 90)) + self.assertEqual(im.getbbox(), (10, 10, 90, 90)) + + im.paste(255, (-10, -10, 110, 110)) + self.assertEqual(im.getbbox(), (0, 0, 100, 100)) + + # 32-bit mode + im = Image.new("RGB", (100, 100), 0) + self.assertEqual(im.getbbox(), None) + + im.paste(255, (10, 25, 90, 75)) + self.assertEqual(im.getbbox(), (10, 25, 90, 75)) + + im.paste(255, (25, 10, 75, 90)) + self.assertEqual(im.getbbox(), (10, 10, 90, 90)) + + im.paste(255, (-10, -10, 110, 110)) + self.assertEqual(im.getbbox(), (0, 0, 100, 100)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_getpixel.py b/test/test_image_getpixel.py new file mode 100644 index 000000000..de5f185ec --- /dev/null +++ b/test/test_image_getpixel.py @@ -0,0 +1,54 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + +Image.USE_CFFI_ACCESS = False + + +class TestImage(PillowTestCase): + + def color(self, mode): + bands = Image.getmodebands(mode) + if bands == 1: + return 1 + else: + return tuple(range(1, bands+1)) + + def check(self, mode, c=None): + if not c: + c = self.color(mode) + + # check putpixel + im = Image.new(mode, (1, 1), None) + im.putpixel((0, 0), c) + self.assertEqual( + im.getpixel((0, 0)), c, + "put/getpixel roundtrip failed for mode %s, color %s" % + (mode, c)) + + # check inital color + im = Image.new(mode, (1, 1), c) + self.assertEqual( + im.getpixel((0, 0)), c, + "initial color failed for mode %s, color %s " % + (mode, self.color)) + + def test_basic(self): + for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F", + "P", "PA", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr"): + self.check(mode) + + def test_signedness(self): + # see https://github.com/python-pillow/Pillow/issues/452 + # pixelaccess is using signed int* instead of uint* + for mode in ("I;16", "I;16B"): + self.check(mode, 2**15-1) + self.check(mode, 2**15) + self.check(mode, 2**15+1) + self.check(mode, 2**16-1) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagefont.py b/test/test_imagefont.py new file mode 100644 index 000000000..17cb38cc2 --- /dev/null +++ b/test/test_imagefont.py @@ -0,0 +1,145 @@ +from helper import unittest, PillowTestCase + +from PIL import Image +from PIL import ImageDraw +from io import BytesIO +import os + +font_path = "Tests/fonts/FreeMono.ttf" +font_size = 20 + + +try: + from PIL import ImageFont + ImageFont.core.getfont # check if freetype is available + + class TestImageFont(PillowTestCase): + + def test_sanity(self): + self.assertRegexpMatches( + ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") + + def test_font_with_name(self): + ImageFont.truetype(font_path, font_size) + self._render(font_path) + self._clean() + + def _font_as_bytes(self): + with open(font_path, 'rb') as f: + font_bytes = BytesIO(f.read()) + return font_bytes + + def test_font_with_filelike(self): + ImageFont.truetype(self._font_as_bytes(), font_size) + self._render(self._font_as_bytes()) + # Usage note: making two fonts from the same buffer fails. + # shared_bytes = self._font_as_bytes() + # self._render(shared_bytes) + # self.assertRaises(Exception, lambda: _render(shared_bytes)) + self._clean() + + def test_font_with_open_file(self): + with open(font_path, 'rb') as f: + self._render(f) + self._clean() + + def test_font_old_parameters(self): + self.assert_warning( + DeprecationWarning, + lambda: ImageFont.truetype(filename=font_path, size=font_size)) + + def _render(self, font): + txt = "Hello World!" + ttf = ImageFont.truetype(font, font_size) + w, h = ttf.getsize(txt) + img = Image.new("RGB", (256, 64), "white") + d = ImageDraw.Draw(img) + d.text((10, 10), txt, font=ttf, fill='black') + + img.save('font.png') + return img + + def _clean(self): + os.unlink('font.png') + + def test_render_equal(self): + img_path = self._render(font_path) + with open(font_path, 'rb') as f: + font_filelike = BytesIO(f.read()) + img_filelike = self._render(font_filelike) + + self.assert_image_equal(img_path, img_filelike) + self._clean() + + def test_render_multiline(self): + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + ttf = ImageFont.truetype(font_path, font_size) + line_spacing = draw.textsize('A', font=ttf)[1] + 8 + lines = ['hey you', 'you are awesome', 'this looks awkward'] + y = 0 + for line in lines: + draw.text((0, y), line, font=ttf) + y += line_spacing + + target = 'Tests/images/multiline_text.png' + target_img = Image.open(target) + + # some versions of freetype have different horizontal spacing. + # setting a tight epsilon, I'm showing the original test failure + # at epsilon = ~38. + self.assert_image_similar(im, target_img, .5) + + def test_rotated_transposed_font(self): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = Image.ROTATE_90 + transposed_font = ImageFont.TransposedFont( + font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # 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[1], box_size_b[0]) + + def test_unrotated_transposed_font(self): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = None + transposed_font = ImageFont.TransposedFont( + font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check boxes a and b are same size + self.assertEqual(box_size_a, box_size_b) + +except ImportError: + class TestImageFont(PillowTestCase): + def test_skip(self): + self.skipTest("ImportError") + + +if __name__ == '__main__': + unittest.main() + +# End of file From 3249d8f3a5728b90ac961f19dc04381d1b390ca2 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 14:01:57 +0300 Subject: [PATCH 113/168] Convert test_image_putpixel.py. More changes needed for test_cffi.py --- Tests/test_cffi.py | 99 ------------------------------ Tests/test_image_putpalette.py | 28 --------- Tests/test_image_putpixel.py | 45 -------------- test/test_cffi.py | 109 +++++++++++++++++++++++++++++++++ test/test_image_putpalette.py | 36 +++++++++++ test/test_image_putpixel.py | 50 +++++++++++++++ 6 files changed, 195 insertions(+), 172 deletions(-) delete mode 100644 Tests/test_cffi.py delete mode 100644 Tests/test_image_putpalette.py delete mode 100644 Tests/test_image_putpixel.py create mode 100644 test/test_cffi.py create mode 100644 test/test_image_putpalette.py create mode 100644 test/test_image_putpixel.py diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py deleted file mode 100644 index 1c0d8d31e..000000000 --- a/Tests/test_cffi.py +++ /dev/null @@ -1,99 +0,0 @@ -from tester import * - -try: - import cffi -except: - skip() - -from PIL import Image, PyAccess - -import test_image_putpixel as put -import test_image_getpixel as get - - -Image.USE_CFFI_ACCESS = True - -def test_put(): - put.test_sanity() - -def test_get(): - get.test_basic() - get.test_signedness() - -def _test_get_access(im): - """ Do we get the same thing as the old pixel access """ - - """ Using private interfaces, forcing a capi access and a pyaccess for the same image """ - caccess = im.im.pixel_access(False) - access = PyAccess.new(im, False) - - w,h = im.size - for x in range(0,w,10): - for y in range(0,h,10): - assert_equal(access[(x,y)], caccess[(x,y)]) - -def test_get_vs_c(): - _test_get_access(lena('RGB')) - _test_get_access(lena('RGBA')) - _test_get_access(lena('L')) - _test_get_access(lena('LA')) - _test_get_access(lena('1')) - _test_get_access(lena('P')) - #_test_get_access(lena('PA')) # PA -- how do I make a PA image??? - _test_get_access(lena('F')) - - im = Image.new('I;16', (10,10), 40000) - _test_get_access(im) - im = Image.new('I;16L', (10,10), 40000) - _test_get_access(im) - im = Image.new('I;16B', (10,10), 40000) - _test_get_access(im) - - im = Image.new('I', (10,10), 40000) - _test_get_access(im) - # These don't actually appear to be modes that I can actually make, - # as unpack sets them directly into the I mode. - #im = Image.new('I;32L', (10,10), -2**10) - #_test_get_access(im) - #im = Image.new('I;32B', (10,10), 2**10) - #_test_get_access(im) - - - -def _test_set_access(im, color): - """ Are we writing the correct bits into the image? """ - - """ Using private interfaces, forcing a capi access and a pyaccess for the same image """ - caccess = im.im.pixel_access(False) - access = PyAccess.new(im, False) - - w,h = im.size - for x in range(0,w,10): - for y in range(0,h,10): - access[(x,y)] = color - assert_equal(color, caccess[(x,y)]) - -def test_set_vs_c(): - _test_set_access(lena('RGB'), (255, 128,0) ) - _test_set_access(lena('RGBA'), (255, 192, 128, 0)) - _test_set_access(lena('L'), 128) - _test_set_access(lena('LA'), (128,128)) - _test_set_access(lena('1'), 255) - _test_set_access(lena('P') , 128) - ##_test_set_access(i, (128,128)) #PA -- undone how to make - _test_set_access(lena('F'), 1024.0) - - im = Image.new('I;16', (10,10), 40000) - _test_set_access(im, 45000) - im = Image.new('I;16L', (10,10), 40000) - _test_set_access(im, 45000) - im = Image.new('I;16B', (10,10), 40000) - _test_set_access(im, 45000) - - - im = Image.new('I', (10,10), 40000) - _test_set_access(im, 45000) -# im = Image.new('I;32L', (10,10), -(2**10)) -# _test_set_access(im, -(2**13)+1) - #im = Image.new('I;32B', (10,10), 2**10) - #_test_set_access(im, 2**13-1) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py deleted file mode 100644 index b7ebb8853..000000000 --- a/Tests/test_image_putpalette.py +++ /dev/null @@ -1,28 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImagePalette - -def test_putpalette(): - def palette(mode): - im = lena(mode).copy() - im.putpalette(list(range(256))*3) - p = im.getpalette() - if p: - return im.mode, p[:10] - return im.mode - assert_exception(ValueError, lambda: palette("1")) - assert_equal(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) - assert_equal(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) - assert_exception(ValueError, lambda: palette("I")) - assert_exception(ValueError, lambda: palette("F")) - assert_exception(ValueError, lambda: palette("RGB")) - assert_exception(ValueError, lambda: palette("RGBA")) - assert_exception(ValueError, lambda: palette("YCbCr")) - -def test_imagepalette(): - im = lena("P") - assert_no_exception(lambda: im.putpalette(ImagePalette.negative())) - assert_no_exception(lambda: im.putpalette(ImagePalette.random())) - assert_no_exception(lambda: im.putpalette(ImagePalette.sepia())) - assert_no_exception(lambda: im.putpalette(ImagePalette.wedge())) diff --git a/Tests/test_image_putpixel.py b/Tests/test_image_putpixel.py deleted file mode 100644 index 5f19237cb..000000000 --- a/Tests/test_image_putpixel.py +++ /dev/null @@ -1,45 +0,0 @@ -from tester import * - -from PIL import Image - -Image.USE_CFFI_ACCESS=False - -def test_sanity(): - - im1 = lena() - im2 = Image.new(im1.mode, im1.size, 0) - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) - - assert_image_equal(im1, im2) - - im2 = Image.new(im1.mode, im1.size, 0) - im2.readonly = 1 - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) - - assert_false(im2.readonly) - assert_image_equal(im1, im2) - - im2 = Image.new(im1.mode, im1.size, 0) - - pix1 = im1.load() - pix2 = im2.load() - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pix2[x, y] = pix1[x, y] - - assert_image_equal(im1, im2) - - - - -# see test_image_getpixel for more tests - diff --git a/test/test_cffi.py b/test/test_cffi.py new file mode 100644 index 000000000..1492af32d --- /dev/null +++ b/test/test_cffi.py @@ -0,0 +1,109 @@ +from helper import unittest, PillowTestCase, lena + +try: + import cffi + + from PIL import Image, PyAccess + + import test_image_putpixel as put + import test_image_getpixel as get + + class TestCffi(PillowTestCase): + + Image.USE_CFFI_ACCESS = True + + def test_put(self): + put.test_sanity() + + def test_get(self): + get.test_basic() + get.test_signedness() + + def _test_get_access(self, im): + """ Do we get the same thing as the old pixel access """ + + """ Using private interfaces, forcing a capi access and a pyaccess + for the same image """ + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + w, h = im.size + for x in range(0, w, 10): + for y in range(0, h, 10): + self.assertEqual(access[(x, y)], caccess[(x, y)]) + + def test_get_vs_c(self): + self._test_get_access(lena('RGB')) + self._test_get_access(lena('RGBA')) + self._test_get_access(lena('L')) + self._test_get_access(lena('LA')) + self._test_get_access(lena('1')) + self._test_get_access(lena('P')) + # PA -- how do I make a PA image??? + # self._test_get_access(lena('PA')) + self._test_get_access(lena('F')) + + im = Image.new('I;16', (10, 10), 40000) + self._test_get_access(im) + im = Image.new('I;16L', (10, 10), 40000) + self._test_get_access(im) + im = Image.new('I;16B', (10, 10), 40000) + self._test_get_access(im) + + im = Image.new('I', (10, 10), 40000) + self._test_get_access(im) + # These don't actually appear to be modes that I can actually make, + # as unpack sets them directly into the I mode. + # im = Image.new('I;32L', (10, 10), -2**10) + # self._test_get_access(im) + # im = Image.new('I;32B', (10, 10), 2**10) + # self._test_get_access(im) + + def _test_set_access(self, im, color): + """ Are we writing the correct bits into the image? """ + + """ Using private interfaces, forcing a capi access and a pyaccess + for the same image """ + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + w, h = im.size + for x in range(0, w, 10): + for y in range(0, h, 10): + access[(x, y)] = color + self.assertEqual(color, caccess[(x, y)]) + + def test_set_vs_c(self): + self._test_set_access(lena('RGB'), (255, 128, 0)) + self._test_set_access(lena('RGBA'), (255, 192, 128, 0)) + self._test_set_access(lena('L'), 128) + self._test_set_access(lena('LA'), (128, 128)) + self._test_set_access(lena('1'), 255) + self._test_set_access(lena('P'), 128) + # self._test_set_access(i, (128, 128)) #PA -- undone how to make + self._test_set_access(lena('F'), 1024.0) + + im = Image.new('I;16', (10, 10), 40000) + self._test_set_access(im, 45000) + im = Image.new('I;16L', (10, 10), 40000) + self._test_set_access(im, 45000) + im = Image.new('I;16B', (10, 10), 40000) + self._test_set_access(im, 45000) + + im = Image.new('I', (10, 10), 40000) + self._test_set_access(im, 45000) + # im = Image.new('I;32L', (10, 10), -(2**10)) + # self._test_set_access(im, -(2**13)+1) + # im = Image.new('I;32B', (10, 10), 2**10) + # self._test_set_access(im, 2**13-1) + +except ImportError: + class TestCffi(PillowTestCase): + def test_skip(self): + self.skipTest("ImportError") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_putpalette.py b/test/test_image_putpalette.py new file mode 100644 index 000000000..b77dcbf00 --- /dev/null +++ b/test/test_image_putpalette.py @@ -0,0 +1,36 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import ImagePalette + + +class TestImage(PillowTestCase): + + def test_putpalette(self): + def palette(mode): + im = lena(mode).copy() + im.putpalette(list(range(256))*3) + p = im.getpalette() + if p: + return im.mode, p[:10] + return im.mode + self.assertRaises(ValueError, lambda: palette("1")) + self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + self.assertRaises(ValueError, lambda: palette("I")) + self.assertRaises(ValueError, lambda: palette("F")) + self.assertRaises(ValueError, lambda: palette("RGB")) + self.assertRaises(ValueError, lambda: palette("RGBA")) + self.assertRaises(ValueError, lambda: palette("YCbCr")) + + def test_imagepalette(self): + im = lena("P") + im.putpalette(ImagePalette.negative()) + im.putpalette(ImagePalette.random()) + im.putpalette(ImagePalette.sepia()) + im.putpalette(ImagePalette.wedge()) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_putpixel.py b/test/test_image_putpixel.py new file mode 100644 index 000000000..a7f5dc2bb --- /dev/null +++ b/test/test_image_putpixel.py @@ -0,0 +1,50 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + +Image.USE_CFFI_ACCESS = False + + +class TestImagePutPixel(PillowTestCase): + + def test_sanity(self): + + im1 = lena() + im2 = Image.new(im1.mode, im1.size, 0) + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + self.assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.readonly = 1 + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + self.assertFalse(im2.readonly) + self.assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + + pix1 = im1.load() + pix2 = im2.load() + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pix2[x, y] = pix1[x, y] + + self.assert_image_equal(im1, im2) + + # see test_image_getpixel for more tests + + +if __name__ == '__main__': + unittest.main() + +# End of file From 2833cb42b45df2025bc408584e8cd8c828f3a6fa Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 14:19:29 +0300 Subject: [PATCH 114/168] Fixes for test_cffi.py --- test/test_cffi.py | 18 +++++++++++------- test/test_image_getpixel.py | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/test/test_cffi.py b/test/test_cffi.py index 1492af32d..638386ac4 100644 --- a/test/test_cffi.py +++ b/test/test_cffi.py @@ -5,19 +5,23 @@ try: from PIL import Image, PyAccess - import test_image_putpixel as put - import test_image_getpixel as get + from test_image_putpixel import TestImagePutPixel + from test_image_getpixel import TestImageGetPixel - class TestCffi(PillowTestCase): + Image.USE_CFFI_ACCESS = True - Image.USE_CFFI_ACCESS = True + class TestCffiPutPixel(TestImagePutPixel): def test_put(self): - put.test_sanity() + self.test_sanity() + + class TestCffiGetPixel(TestImageGetPixel): def test_get(self): - get.test_basic() - get.test_signedness() + self.test_basic() + self.test_signedness() + + class TestCffi(PillowTestCase): def _test_get_access(self, im): """ Do we get the same thing as the old pixel access """ diff --git a/test/test_image_getpixel.py b/test/test_image_getpixel.py index de5f185ec..9749ba836 100644 --- a/test/test_image_getpixel.py +++ b/test/test_image_getpixel.py @@ -5,7 +5,7 @@ from PIL import Image Image.USE_CFFI_ACCESS = False -class TestImage(PillowTestCase): +class TestImageGetPixel(PillowTestCase): def color(self, mode): bands = Image.getmodebands(mode) From 07aa1a56bbddd79128a81584eb0ec8f903c5192f Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 15:31:22 +0300 Subject: [PATCH 115/168] Save test_cffi for another day --- Tests/test_cffi.py | 99 ++++++++++++++++++++++++++++++ Tests/test_image_getpixel.py | 49 +++++++++++++++ Tests/test_image_putpixel.py | 45 ++++++++++++++ test/test_cffi.py | 113 ----------------------------------- test/test_image_getpixel.py | 54 ----------------- test/test_image_putpixel.py | 50 ---------------- 6 files changed, 193 insertions(+), 217 deletions(-) create mode 100644 Tests/test_cffi.py create mode 100644 Tests/test_image_getpixel.py create mode 100644 Tests/test_image_putpixel.py delete mode 100644 test/test_cffi.py delete mode 100644 test/test_image_getpixel.py delete mode 100644 test/test_image_putpixel.py diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py new file mode 100644 index 000000000..1c0d8d31e --- /dev/null +++ b/Tests/test_cffi.py @@ -0,0 +1,99 @@ +from tester import * + +try: + import cffi +except: + skip() + +from PIL import Image, PyAccess + +import test_image_putpixel as put +import test_image_getpixel as get + + +Image.USE_CFFI_ACCESS = True + +def test_put(): + put.test_sanity() + +def test_get(): + get.test_basic() + get.test_signedness() + +def _test_get_access(im): + """ Do we get the same thing as the old pixel access """ + + """ Using private interfaces, forcing a capi access and a pyaccess for the same image """ + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + w,h = im.size + for x in range(0,w,10): + for y in range(0,h,10): + assert_equal(access[(x,y)], caccess[(x,y)]) + +def test_get_vs_c(): + _test_get_access(lena('RGB')) + _test_get_access(lena('RGBA')) + _test_get_access(lena('L')) + _test_get_access(lena('LA')) + _test_get_access(lena('1')) + _test_get_access(lena('P')) + #_test_get_access(lena('PA')) # PA -- how do I make a PA image??? + _test_get_access(lena('F')) + + im = Image.new('I;16', (10,10), 40000) + _test_get_access(im) + im = Image.new('I;16L', (10,10), 40000) + _test_get_access(im) + im = Image.new('I;16B', (10,10), 40000) + _test_get_access(im) + + im = Image.new('I', (10,10), 40000) + _test_get_access(im) + # These don't actually appear to be modes that I can actually make, + # as unpack sets them directly into the I mode. + #im = Image.new('I;32L', (10,10), -2**10) + #_test_get_access(im) + #im = Image.new('I;32B', (10,10), 2**10) + #_test_get_access(im) + + + +def _test_set_access(im, color): + """ Are we writing the correct bits into the image? """ + + """ Using private interfaces, forcing a capi access and a pyaccess for the same image """ + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + w,h = im.size + for x in range(0,w,10): + for y in range(0,h,10): + access[(x,y)] = color + assert_equal(color, caccess[(x,y)]) + +def test_set_vs_c(): + _test_set_access(lena('RGB'), (255, 128,0) ) + _test_set_access(lena('RGBA'), (255, 192, 128, 0)) + _test_set_access(lena('L'), 128) + _test_set_access(lena('LA'), (128,128)) + _test_set_access(lena('1'), 255) + _test_set_access(lena('P') , 128) + ##_test_set_access(i, (128,128)) #PA -- undone how to make + _test_set_access(lena('F'), 1024.0) + + im = Image.new('I;16', (10,10), 40000) + _test_set_access(im, 45000) + im = Image.new('I;16L', (10,10), 40000) + _test_set_access(im, 45000) + im = Image.new('I;16B', (10,10), 40000) + _test_set_access(im, 45000) + + + im = Image.new('I', (10,10), 40000) + _test_set_access(im, 45000) +# im = Image.new('I;32L', (10,10), -(2**10)) +# _test_set_access(im, -(2**13)+1) + #im = Image.new('I;32B', (10,10), 2**10) + #_test_set_access(im, 2**13-1) diff --git a/Tests/test_image_getpixel.py b/Tests/test_image_getpixel.py new file mode 100644 index 000000000..da3d8115e --- /dev/null +++ b/Tests/test_image_getpixel.py @@ -0,0 +1,49 @@ +from tester import * + +from PIL import Image + +Image.USE_CFFI_ACCESS=False + +def color(mode): + bands = Image.getmodebands(mode) + if bands == 1: + return 1 + else: + return tuple(range(1, bands+1)) + + + +def check(mode, c=None): + if not c: + c = color(mode) + + #check putpixel + im = Image.new(mode, (1, 1), None) + im.putpixel((0, 0), c) + assert_equal(im.getpixel((0, 0)), c, + "put/getpixel roundtrip failed for mode %s, color %s" % + (mode, c)) + + # check inital color + im = Image.new(mode, (1, 1), c) + assert_equal(im.getpixel((0, 0)), c, + "initial color failed for mode %s, color %s " % + (mode, color)) + +def test_basic(): + for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F", + "P", "PA", "RGB", "RGBA", "RGBX", "CMYK","YCbCr"): + check(mode) + +def test_signedness(): + # see https://github.com/python-pillow/Pillow/issues/452 + # pixelaccess is using signed int* instead of uint* + for mode in ("I;16", "I;16B"): + check(mode, 2**15-1) + check(mode, 2**15) + check(mode, 2**15+1) + check(mode, 2**16-1) + + + + diff --git a/Tests/test_image_putpixel.py b/Tests/test_image_putpixel.py new file mode 100644 index 000000000..5f19237cb --- /dev/null +++ b/Tests/test_image_putpixel.py @@ -0,0 +1,45 @@ +from tester import * + +from PIL import Image + +Image.USE_CFFI_ACCESS=False + +def test_sanity(): + + im1 = lena() + im2 = Image.new(im1.mode, im1.size, 0) + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.readonly = 1 + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + assert_false(im2.readonly) + assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + + pix1 = im1.load() + pix2 = im2.load() + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pix2[x, y] = pix1[x, y] + + assert_image_equal(im1, im2) + + + + +# see test_image_getpixel for more tests + diff --git a/test/test_cffi.py b/test/test_cffi.py deleted file mode 100644 index 638386ac4..000000000 --- a/test/test_cffi.py +++ /dev/null @@ -1,113 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -try: - import cffi - - from PIL import Image, PyAccess - - from test_image_putpixel import TestImagePutPixel - from test_image_getpixel import TestImageGetPixel - - Image.USE_CFFI_ACCESS = True - - class TestCffiPutPixel(TestImagePutPixel): - - def test_put(self): - self.test_sanity() - - class TestCffiGetPixel(TestImageGetPixel): - - def test_get(self): - self.test_basic() - self.test_signedness() - - class TestCffi(PillowTestCase): - - def _test_get_access(self, im): - """ Do we get the same thing as the old pixel access """ - - """ Using private interfaces, forcing a capi access and a pyaccess - for the same image """ - caccess = im.im.pixel_access(False) - access = PyAccess.new(im, False) - - w, h = im.size - for x in range(0, w, 10): - for y in range(0, h, 10): - self.assertEqual(access[(x, y)], caccess[(x, y)]) - - def test_get_vs_c(self): - self._test_get_access(lena('RGB')) - self._test_get_access(lena('RGBA')) - self._test_get_access(lena('L')) - self._test_get_access(lena('LA')) - self._test_get_access(lena('1')) - self._test_get_access(lena('P')) - # PA -- how do I make a PA image??? - # self._test_get_access(lena('PA')) - self._test_get_access(lena('F')) - - im = Image.new('I;16', (10, 10), 40000) - self._test_get_access(im) - im = Image.new('I;16L', (10, 10), 40000) - self._test_get_access(im) - im = Image.new('I;16B', (10, 10), 40000) - self._test_get_access(im) - - im = Image.new('I', (10, 10), 40000) - self._test_get_access(im) - # These don't actually appear to be modes that I can actually make, - # as unpack sets them directly into the I mode. - # im = Image.new('I;32L', (10, 10), -2**10) - # self._test_get_access(im) - # im = Image.new('I;32B', (10, 10), 2**10) - # self._test_get_access(im) - - def _test_set_access(self, im, color): - """ Are we writing the correct bits into the image? """ - - """ Using private interfaces, forcing a capi access and a pyaccess - for the same image """ - caccess = im.im.pixel_access(False) - access = PyAccess.new(im, False) - - w, h = im.size - for x in range(0, w, 10): - for y in range(0, h, 10): - access[(x, y)] = color - self.assertEqual(color, caccess[(x, y)]) - - def test_set_vs_c(self): - self._test_set_access(lena('RGB'), (255, 128, 0)) - self._test_set_access(lena('RGBA'), (255, 192, 128, 0)) - self._test_set_access(lena('L'), 128) - self._test_set_access(lena('LA'), (128, 128)) - self._test_set_access(lena('1'), 255) - self._test_set_access(lena('P'), 128) - # self._test_set_access(i, (128, 128)) #PA -- undone how to make - self._test_set_access(lena('F'), 1024.0) - - im = Image.new('I;16', (10, 10), 40000) - self._test_set_access(im, 45000) - im = Image.new('I;16L', (10, 10), 40000) - self._test_set_access(im, 45000) - im = Image.new('I;16B', (10, 10), 40000) - self._test_set_access(im, 45000) - - im = Image.new('I', (10, 10), 40000) - self._test_set_access(im, 45000) - # im = Image.new('I;32L', (10, 10), -(2**10)) - # self._test_set_access(im, -(2**13)+1) - # im = Image.new('I;32B', (10, 10), 2**10) - # self._test_set_access(im, 2**13-1) - -except ImportError: - class TestCffi(PillowTestCase): - def test_skip(self): - self.skipTest("ImportError") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_getpixel.py b/test/test_image_getpixel.py deleted file mode 100644 index 9749ba836..000000000 --- a/test/test_image_getpixel.py +++ /dev/null @@ -1,54 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - -Image.USE_CFFI_ACCESS = False - - -class TestImageGetPixel(PillowTestCase): - - def color(self, mode): - bands = Image.getmodebands(mode) - if bands == 1: - return 1 - else: - return tuple(range(1, bands+1)) - - def check(self, mode, c=None): - if not c: - c = self.color(mode) - - # check putpixel - im = Image.new(mode, (1, 1), None) - im.putpixel((0, 0), c) - self.assertEqual( - im.getpixel((0, 0)), c, - "put/getpixel roundtrip failed for mode %s, color %s" % - (mode, c)) - - # check inital color - im = Image.new(mode, (1, 1), c) - self.assertEqual( - im.getpixel((0, 0)), c, - "initial color failed for mode %s, color %s " % - (mode, self.color)) - - def test_basic(self): - for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F", - "P", "PA", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr"): - self.check(mode) - - def test_signedness(self): - # see https://github.com/python-pillow/Pillow/issues/452 - # pixelaccess is using signed int* instead of uint* - for mode in ("I;16", "I;16B"): - self.check(mode, 2**15-1) - self.check(mode, 2**15) - self.check(mode, 2**15+1) - self.check(mode, 2**16-1) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_putpixel.py b/test/test_image_putpixel.py deleted file mode 100644 index a7f5dc2bb..000000000 --- a/test/test_image_putpixel.py +++ /dev/null @@ -1,50 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - -Image.USE_CFFI_ACCESS = False - - -class TestImagePutPixel(PillowTestCase): - - def test_sanity(self): - - im1 = lena() - im2 = Image.new(im1.mode, im1.size, 0) - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) - - self.assert_image_equal(im1, im2) - - im2 = Image.new(im1.mode, im1.size, 0) - im2.readonly = 1 - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) - - self.assertFalse(im2.readonly) - self.assert_image_equal(im1, im2) - - im2 = Image.new(im1.mode, im1.size, 0) - - pix1 = im1.load() - pix2 = im2.load() - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pix2[x, y] = pix1[x, y] - - self.assert_image_equal(im1, im2) - - # see test_image_getpixel for more tests - - -if __name__ == '__main__': - unittest.main() - -# End of file From 8a870c0ee9af404685c3cbf2787169b00d99d397 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 16:13:58 +0300 Subject: [PATCH 116/168] Convert more tests --- Tests/test_bmp_reference.py | 86 -------------------------------- Tests/test_file_psd.py | 14 ------ Tests/test_image_getcolors.py | 64 ------------------------ Tests/test_image_rotate.py | 15 ------ Tests/test_image_thumbnail.py | 36 -------------- Tests/test_imageops.py | 81 ------------------------------ test/mini.py | 5 ++ test/test_bmp_reference.py | 94 +++++++++++++++++++++++++++++++++++ test/test_file_psd.py | 23 +++++++++ test/test_image_getcolors.py | 74 +++++++++++++++++++++++++++ test/test_image_rotate.py | 22 ++++++++ test/test_image_thumbnail.py | 43 ++++++++++++++++ test/test_imageops.py | 85 +++++++++++++++++++++++++++++++ 13 files changed, 346 insertions(+), 296 deletions(-) delete mode 100644 Tests/test_bmp_reference.py delete mode 100644 Tests/test_file_psd.py delete mode 100644 Tests/test_image_getcolors.py delete mode 100644 Tests/test_image_rotate.py delete mode 100644 Tests/test_image_thumbnail.py delete mode 100644 Tests/test_imageops.py create mode 100644 test/mini.py create mode 100644 test/test_bmp_reference.py create mode 100644 test/test_file_psd.py create mode 100644 test/test_image_getcolors.py create mode 100644 test/test_image_rotate.py create mode 100644 test/test_image_thumbnail.py create mode 100644 test/test_imageops.py diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py deleted file mode 100644 index 99818229f..000000000 --- a/Tests/test_bmp_reference.py +++ /dev/null @@ -1,86 +0,0 @@ -from tester import * - -from PIL import Image -import os - -base = os.path.join('Tests', 'images', 'bmp') - - -def get_files(d, ext='.bmp'): - return [os.path.join(base,d,f) for f - in os.listdir(os.path.join(base, d)) if ext in f] - -def test_bad(): - """ These shouldn't crash/dos, but they shouldn't return anything either """ - for f in get_files('b'): - try: - im = Image.open(f) - im.load() - except Exception as msg: - pass - # print ("Bad Image %s: %s" %(f,msg)) - -def test_questionable(): - """ These shouldn't crash/dos, but its not well defined that these are in spec """ - for f in get_files('q'): - try: - im = Image.open(f) - im.load() - except Exception as msg: - pass - # print ("Bad Image %s: %s" %(f,msg)) - - -def test_good(): - """ These should all work. There's a set of target files in the - html directory that we can compare against. """ - - # Target files, if they're not just replacing the extension - file_map = { 'pal1wb.bmp': 'pal1.png', - 'pal4rle.bmp': 'pal4.png', - 'pal8-0.bmp': 'pal8.png', - 'pal8rle.bmp': 'pal8.png', - 'pal8topdown.bmp': 'pal8.png', - 'pal8nonsquare.bmp': 'pal8nonsquare-v.png', - 'pal8os2.bmp': 'pal8.png', - 'pal8os2sp.bmp': 'pal8.png', - 'pal8os2v2.bmp': 'pal8.png', - 'pal8os2v2-16.bmp': 'pal8.png', - 'pal8v4.bmp': 'pal8.png', - 'pal8v5.bmp': 'pal8.png', - 'rgb16-565pal.bmp': 'rgb16-565.png', - 'rgb24pal.bmp': 'rgb24.png', - 'rgb32.bmp': 'rgb24.png', - 'rgb32bf.bmp': 'rgb24.png' - } - - def get_compare(f): - (head, name) = os.path.split(f) - if name in file_map: - return os.path.join(base, 'html', file_map[name]) - (name,ext) = os.path.splitext(name) - return os.path.join(base, 'html', "%s.png"%name) - - for f in get_files('g'): - try: - im = Image.open(f) - im.load() - compare = Image.open(get_compare(f)) - compare.load() - if im.mode == 'P': - # assert image similar doesn't really work - # with paletized image, since the palette might - # be differently ordered for an equivalent image. - im = im.convert('RGBA') - compare = im.convert('RGBA') - assert_image_similar(im, compare,5) - - - except Exception as msg: - # there are three here that are unsupported: - unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), - os.path.join(base, 'g', 'pal8rle.bmp'), - os.path.join(base, 'g', 'pal4rle.bmp')) - if f not in unsupported: - assert_true(False, "Unsupported Image %s: %s" %(f,msg)) - diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py deleted file mode 100644 index ef2d40594..000000000 --- a/Tests/test_file_psd.py +++ /dev/null @@ -1,14 +0,0 @@ -from tester import * - -from PIL import Image - -# sample ppm stream -file = "Images/lena.psd" -data = open(file, "rb").read() - -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "PSD") diff --git a/Tests/test_image_getcolors.py b/Tests/test_image_getcolors.py deleted file mode 100644 index 2429f9350..000000000 --- a/Tests/test_image_getcolors.py +++ /dev/null @@ -1,64 +0,0 @@ -from tester import * - -from PIL import Image - -def test_getcolors(): - - def getcolors(mode, limit=None): - im = lena(mode) - if limit: - colors = im.getcolors(limit) - else: - colors = im.getcolors() - if colors: - return len(colors) - return None - - assert_equal(getcolors("1"), 2) - assert_equal(getcolors("L"), 193) - assert_equal(getcolors("I"), 193) - assert_equal(getcolors("F"), 193) - assert_equal(getcolors("P"), 54) # fixed palette - assert_equal(getcolors("RGB"), None) - assert_equal(getcolors("RGBA"), None) - assert_equal(getcolors("CMYK"), None) - assert_equal(getcolors("YCbCr"), None) - - assert_equal(getcolors("L", 128), None) - assert_equal(getcolors("L", 1024), 193) - - assert_equal(getcolors("RGB", 8192), None) - assert_equal(getcolors("RGB", 16384), 14836) - assert_equal(getcolors("RGB", 100000), 14836) - - assert_equal(getcolors("RGBA", 16384), 14836) - assert_equal(getcolors("CMYK", 16384), 14836) - assert_equal(getcolors("YCbCr", 16384), 11995) - -# -------------------------------------------------------------------- - -def test_pack(): - # Pack problems for small tables (@PIL209) - - im = lena().quantize(3).convert("RGB") - - expected = [(3236, (227, 183, 147)), (6297, (143, 84, 81)), (6851, (208, 143, 112))] - - A = im.getcolors(maxcolors=2) - assert_equal(A, None) - - A = im.getcolors(maxcolors=3) - A.sort() - assert_equal(A, expected) - - A = im.getcolors(maxcolors=4) - A.sort() - assert_equal(A, expected) - - A = im.getcolors(maxcolors=8) - A.sort() - assert_equal(A, expected) - - A = im.getcolors(maxcolors=16) - A.sort() - assert_equal(A, expected) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py deleted file mode 100644 index 5e4782c87..000000000 --- a/Tests/test_image_rotate.py +++ /dev/null @@ -1,15 +0,0 @@ -from tester import * - -from PIL import Image - -def test_rotate(): - def rotate(mode): - im = lena(mode) - out = im.rotate(45) - assert_equal(out.mode, mode) - assert_equal(out.size, im.size) # default rotate clips output - out = im.rotate(45, expand=1) - assert_equal(out.mode, mode) - assert_true(out.size != im.size) - for mode in "1", "P", "L", "RGB", "I", "F": - yield_test(rotate, mode) diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py deleted file mode 100644 index 871dd1f54..000000000 --- a/Tests/test_image_thumbnail.py +++ /dev/null @@ -1,36 +0,0 @@ -from tester import * - -from PIL import Image - -def test_sanity(): - - im = lena() - im.thumbnail((100, 100)) - - assert_image(im, im.mode, (100, 100)) - -def test_aspect(): - - im = lena() - im.thumbnail((100, 100)) - assert_image(im, im.mode, (100, 100)) - - im = lena().resize((128, 256)) - im.thumbnail((100, 100)) - assert_image(im, im.mode, (50, 100)) - - im = lena().resize((128, 256)) - im.thumbnail((50, 100)) - assert_image(im, im.mode, (50, 100)) - - im = lena().resize((256, 128)) - im.thumbnail((100, 100)) - assert_image(im, im.mode, (100, 50)) - - im = lena().resize((256, 128)) - im.thumbnail((100, 50)) - assert_image(im, im.mode, (100, 50)) - - im = lena().resize((128, 128)) - im.thumbnail((100, 100)) - assert_image(im, im.mode, (100, 100)) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py deleted file mode 100644 index 8ed5ccefa..000000000 --- a/Tests/test_imageops.py +++ /dev/null @@ -1,81 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageOps - -class Deformer: - def getmesh(self, im): - x, y = im.size - return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] - -deformer = Deformer() - -def test_sanity(): - - ImageOps.autocontrast(lena("L")) - ImageOps.autocontrast(lena("RGB")) - - ImageOps.autocontrast(lena("L"), cutoff=10) - ImageOps.autocontrast(lena("L"), ignore=[0, 255]) - - ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) - ImageOps.colorize(lena("L"), "black", "white") - - ImageOps.crop(lena("L"), 1) - ImageOps.crop(lena("RGB"), 1) - - ImageOps.deform(lena("L"), deformer) - ImageOps.deform(lena("RGB"), deformer) - - ImageOps.equalize(lena("L")) - ImageOps.equalize(lena("RGB")) - - ImageOps.expand(lena("L"), 1) - ImageOps.expand(lena("RGB"), 1) - ImageOps.expand(lena("L"), 2, "blue") - ImageOps.expand(lena("RGB"), 2, "blue") - - ImageOps.fit(lena("L"), (128, 128)) - ImageOps.fit(lena("RGB"), (128, 128)) - - ImageOps.flip(lena("L")) - ImageOps.flip(lena("RGB")) - - ImageOps.grayscale(lena("L")) - ImageOps.grayscale(lena("RGB")) - - ImageOps.invert(lena("L")) - ImageOps.invert(lena("RGB")) - - ImageOps.mirror(lena("L")) - ImageOps.mirror(lena("RGB")) - - ImageOps.posterize(lena("L"), 4) - ImageOps.posterize(lena("RGB"), 4) - - ImageOps.solarize(lena("L")) - ImageOps.solarize(lena("RGB")) - - success() - -def test_1pxfit(): - # Division by zero in equalize if image is 1 pixel high - newimg = ImageOps.fit(lena("RGB").resize((1,1)), (35,35)) - assert_equal(newimg.size,(35,35)) - - newimg = ImageOps.fit(lena("RGB").resize((1,100)), (35,35)) - assert_equal(newimg.size,(35,35)) - - newimg = ImageOps.fit(lena("RGB").resize((100,1)), (35,35)) - assert_equal(newimg.size,(35,35)) - -def test_pil163(): - # Division by zero in equalize if < 255 pixels in image (@PIL163) - - i = lena("RGB").resize((15, 16)) - - ImageOps.equalize(i.convert("L")) - ImageOps.equalize(i.convert("P")) - ImageOps.equalize(i.convert("RGB")) - - success() diff --git a/test/mini.py b/test/mini.py new file mode 100644 index 000000000..c9f1665ab --- /dev/null +++ b/test/mini.py @@ -0,0 +1,5 @@ +from test_image_putpixel import TestImagePutPixel as put +from test_image_getpixel import TestImageGetPixel as get + +dir(get) +get.test_basic() \ No newline at end of file diff --git a/test/test_bmp_reference.py b/test/test_bmp_reference.py new file mode 100644 index 000000000..ed012e7c8 --- /dev/null +++ b/test/test_bmp_reference.py @@ -0,0 +1,94 @@ +from helper import unittest, PillowTestCase + +from PIL import Image +import os + +base = os.path.join('Tests', 'images', 'bmp') + + +class TestImage(PillowTestCase): + + def get_files(self, d, ext='.bmp'): + return [os.path.join(base, d, f) for f + in os.listdir(os.path.join(base, d)) if ext in f] + + def test_bad(self): + """ These shouldn't crash/dos, but they shouldn't return anything + either """ + for f in self.get_files('b'): + try: + im = Image.open(f) + im.load() + except Exception: # as msg: + pass + # print ("Bad Image %s: %s" %(f,msg)) + + def test_questionable(self): + """ These shouldn't crash/dos, but its not well defined that these + are in spec """ + for f in self.get_files('q'): + try: + im = Image.open(f) + im.load() + except Exception: # as msg: + pass + # print ("Bad Image %s: %s" %(f,msg)) + + def test_good(self): + """ These should all work. There's a set of target files in the + html directory that we can compare against. """ + + # Target files, if they're not just replacing the extension + file_map = {'pal1wb.bmp': 'pal1.png', + 'pal4rle.bmp': 'pal4.png', + 'pal8-0.bmp': 'pal8.png', + 'pal8rle.bmp': 'pal8.png', + 'pal8topdown.bmp': 'pal8.png', + 'pal8nonsquare.bmp': 'pal8nonsquare-v.png', + 'pal8os2.bmp': 'pal8.png', + 'pal8os2sp.bmp': 'pal8.png', + 'pal8os2v2.bmp': 'pal8.png', + 'pal8os2v2-16.bmp': 'pal8.png', + 'pal8v4.bmp': 'pal8.png', + 'pal8v5.bmp': 'pal8.png', + 'rgb16-565pal.bmp': 'rgb16-565.png', + 'rgb24pal.bmp': 'rgb24.png', + 'rgb32.bmp': 'rgb24.png', + 'rgb32bf.bmp': 'rgb24.png' + } + + def get_compare(f): + (head, name) = os.path.split(f) + if name in file_map: + return os.path.join(base, 'html', file_map[name]) + (name, ext) = os.path.splitext(name) + return os.path.join(base, 'html', "%s.png" % name) + + for f in self.get_files('g'): + try: + im = Image.open(f) + im.load() + compare = Image.open(get_compare(f)) + compare.load() + if im.mode == 'P': + # assert image similar doesn't really work + # with paletized image, since the palette might + # be differently ordered for an equivalent image. + im = im.convert('RGBA') + compare = im.convert('RGBA') + self.assert_image_similar(im, compare, 5) + + except Exception as msg: + # there are three here that are unsupported: + unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), + os.path.join(base, 'g', 'pal8rle.bmp'), + os.path.join(base, 'g', 'pal4rle.bmp')) + if f not in unsupported: + self.assertTrue( + False, "Unsupported Image %s: %s" % (f, msg)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_file_psd.py b/test/test_file_psd.py new file mode 100644 index 000000000..de3d6f33d --- /dev/null +++ b/test/test_file_psd.py @@ -0,0 +1,23 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + +# sample ppm stream +file = "Images/lena.psd" +data = open(file, "rb").read() + + +class TestImagePsd(PillowTestCase): + + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "PSD") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_getcolors.py b/test/test_image_getcolors.py new file mode 100644 index 000000000..cfc827b28 --- /dev/null +++ b/test/test_image_getcolors.py @@ -0,0 +1,74 @@ +from helper import unittest, PillowTestCase, lena + + +class TestImage(PillowTestCase): + + def test_getcolors(self): + + def getcolors(mode, limit=None): + im = lena(mode) + if limit: + colors = im.getcolors(limit) + else: + colors = im.getcolors() + if colors: + return len(colors) + return None + + self.assertEqual(getcolors("1"), 2) + self.assertEqual(getcolors("L"), 193) + self.assertEqual(getcolors("I"), 193) + self.assertEqual(getcolors("F"), 193) + self.assertEqual(getcolors("P"), 54) # fixed palette + self.assertEqual(getcolors("RGB"), None) + self.assertEqual(getcolors("RGBA"), None) + self.assertEqual(getcolors("CMYK"), None) + self.assertEqual(getcolors("YCbCr"), None) + + self.assertEqual(getcolors("L", 128), None) + self.assertEqual(getcolors("L", 1024), 193) + + self.assertEqual(getcolors("RGB", 8192), None) + self.assertEqual(getcolors("RGB", 16384), 14836) + self.assertEqual(getcolors("RGB", 100000), 14836) + + self.assertEqual(getcolors("RGBA", 16384), 14836) + self.assertEqual(getcolors("CMYK", 16384), 14836) + self.assertEqual(getcolors("YCbCr", 16384), 11995) + + # -------------------------------------------------------------------- + + def test_pack(self): + # Pack problems for small tables (@PIL209) + + im = lena().quantize(3).convert("RGB") + + expected = [ + (3236, (227, 183, 147)), + (6297, (143, 84, 81)), + (6851, (208, 143, 112))] + + A = im.getcolors(maxcolors=2) + self.assertEqual(A, None) + + A = im.getcolors(maxcolors=3) + A.sort() + self.assertEqual(A, expected) + + A = im.getcolors(maxcolors=4) + A.sort() + self.assertEqual(A, expected) + + A = im.getcolors(maxcolors=8) + A.sort() + self.assertEqual(A, expected) + + A = im.getcolors(maxcolors=16) + A.sort() + self.assertEqual(A, expected) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_rotate.py b/test/test_image_rotate.py new file mode 100644 index 000000000..531fdd63f --- /dev/null +++ b/test/test_image_rotate.py @@ -0,0 +1,22 @@ +from helper import unittest, PillowTestCase, lena + + +class TestImageRotate(PillowTestCase): + + def test_rotate(self): + def rotate(mode): + im = lena(mode) + out = im.rotate(45) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) # default rotate clips output + out = im.rotate(45, expand=1) + self.assertEqual(out.mode, mode) + self.assertNotEqual(out.size, im.size) + for mode in "1", "P", "L", "RGB", "I", "F": + rotate(mode) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_thumbnail.py b/test/test_image_thumbnail.py new file mode 100644 index 000000000..ee49be43e --- /dev/null +++ b/test/test_image_thumbnail.py @@ -0,0 +1,43 @@ +from helper import unittest, PillowTestCase, lena + + +class TestImageThumbnail(PillowTestCase): + + def test_sanity(self): + + im = lena() + im.thumbnail((100, 100)) + + self.assert_image(im, im.mode, (100, 100)) + + def test_aspect(self): + + im = lena() + im.thumbnail((100, 100)) + self.assert_image(im, im.mode, (100, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((100, 100)) + self.assert_image(im, im.mode, (50, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((50, 100)) + self.assert_image(im, im.mode, (50, 100)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 100)) + self.assert_image(im, im.mode, (100, 50)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 50)) + self.assert_image(im, im.mode, (100, 50)) + + im = lena().resize((128, 128)) + im.thumbnail((100, 100)) + self.assert_image(im, im.mode, (100, 100)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imageops.py b/test/test_imageops.py new file mode 100644 index 000000000..a4a94ca4d --- /dev/null +++ b/test/test_imageops.py @@ -0,0 +1,85 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import ImageOps + + +class TestImageOps(PillowTestCase): + + class Deformer: + def getmesh(self, im): + x, y = im.size + return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] + + deformer = Deformer() + + def test_sanity(self): + + ImageOps.autocontrast(lena("L")) + ImageOps.autocontrast(lena("RGB")) + + ImageOps.autocontrast(lena("L"), cutoff=10) + ImageOps.autocontrast(lena("L"), ignore=[0, 255]) + + ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) + ImageOps.colorize(lena("L"), "black", "white") + + ImageOps.crop(lena("L"), 1) + ImageOps.crop(lena("RGB"), 1) + + ImageOps.deform(lena("L"), self.deformer) + ImageOps.deform(lena("RGB"), self.deformer) + + ImageOps.equalize(lena("L")) + ImageOps.equalize(lena("RGB")) + + ImageOps.expand(lena("L"), 1) + ImageOps.expand(lena("RGB"), 1) + ImageOps.expand(lena("L"), 2, "blue") + ImageOps.expand(lena("RGB"), 2, "blue") + + ImageOps.fit(lena("L"), (128, 128)) + ImageOps.fit(lena("RGB"), (128, 128)) + + ImageOps.flip(lena("L")) + ImageOps.flip(lena("RGB")) + + ImageOps.grayscale(lena("L")) + ImageOps.grayscale(lena("RGB")) + + ImageOps.invert(lena("L")) + ImageOps.invert(lena("RGB")) + + ImageOps.mirror(lena("L")) + ImageOps.mirror(lena("RGB")) + + ImageOps.posterize(lena("L"), 4) + ImageOps.posterize(lena("RGB"), 4) + + ImageOps.solarize(lena("L")) + ImageOps.solarize(lena("RGB")) + + def test_1pxfit(self): + # Division by zero in equalize if image is 1 pixel high + newimg = ImageOps.fit(lena("RGB").resize((1, 1)), (35, 35)) + self.assertEqual(newimg.size, (35, 35)) + + newimg = ImageOps.fit(lena("RGB").resize((1, 100)), (35, 35)) + self.assertEqual(newimg.size, (35, 35)) + + newimg = ImageOps.fit(lena("RGB").resize((100, 1)), (35, 35)) + self.assertEqual(newimg.size, (35, 35)) + + def test_pil163(self): + # Division by zero in equalize if < 255 pixels in image (@PIL163) + + i = lena("RGB").resize((15, 16)) + + ImageOps.equalize(i.convert("L")) + ImageOps.equalize(i.convert("P")) + ImageOps.equalize(i.convert("RGB")) + + +if __name__ == '__main__': + unittest.main() + +# End of file From d18c406d394bc87737f9136280e45d36d31988fe Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 17:27:03 +0300 Subject: [PATCH 117/168] Convert test_imagecolor.py --- Tests/test_imagecolor.py | 54 ------------------------------ test/test_imagecolor.py | 71 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 54 deletions(-) delete mode 100644 Tests/test_imagecolor.py create mode 100644 test/test_imagecolor.py diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py deleted file mode 100644 index c67c20255..000000000 --- a/Tests/test_imagecolor.py +++ /dev/null @@ -1,54 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageColor - -# -------------------------------------------------------------------- -# sanity - -assert_equal((255, 0, 0), ImageColor.getrgb("#f00")) -assert_equal((255, 0, 0), ImageColor.getrgb("#ff0000")) -assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) -assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)")) -assert_equal((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) -assert_equal((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) -assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)")) -assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) -assert_equal((255, 0, 0), ImageColor.getrgb("red")) - -# -------------------------------------------------------------------- -# look for rounding errors (based on code by Tim Hatch) - -for color in list(ImageColor.colormap.keys()): - expected = Image.new("RGB", (1, 1), color).convert("L").getpixel((0, 0)) - actual = Image.new("L", (1, 1), color).getpixel((0, 0)) - assert_equal(expected, actual) - -assert_equal((0, 0, 0), ImageColor.getcolor("black", "RGB")) -assert_equal((255, 255, 255), ImageColor.getcolor("white", "RGB")) -assert_equal((0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB")) -Image.new("RGB", (1, 1), "white") - -assert_equal((0, 0, 0, 255), ImageColor.getcolor("black", "RGBA")) -assert_equal((255, 255, 255, 255), ImageColor.getcolor("white", "RGBA")) -assert_equal((0, 255, 115, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGBA")) -Image.new("RGBA", (1, 1), "white") - -assert_equal(0, ImageColor.getcolor("black", "L")) -assert_equal(255, ImageColor.getcolor("white", "L")) -assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) -Image.new("L", (1, 1), "white") - -assert_equal(0, ImageColor.getcolor("black", "1")) -assert_equal(255, ImageColor.getcolor("white", "1")) -# The following test is wrong, but is current behavior -# The correct result should be 255 due to the mode 1 -assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) -# Correct behavior -# assert_equal(255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) -Image.new("1", (1, 1), "white") - -assert_equal((0, 255), ImageColor.getcolor("black", "LA")) -assert_equal((255, 255), ImageColor.getcolor("white", "LA")) -assert_equal((162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) -Image.new("LA", (1, 1), "white") diff --git a/test/test_imagecolor.py b/test/test_imagecolor.py new file mode 100644 index 000000000..5d8944852 --- /dev/null +++ b/test/test_imagecolor.py @@ -0,0 +1,71 @@ +from helper import unittest, PillowTestCase + +from PIL import Image +from PIL import ImageColor + + +class TestImageColor(PillowTestCase): + + def test_sanity(self): + self.assertEqual((255, 0, 0), ImageColor.getrgb("#f00")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("#ff0000")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) + self.assertEqual((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)")) + self.assertEqual( + (255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("red")) + + # look for rounding errors (based on code by Tim Hatch) + def test_rounding_errors(self): + + for color in list(ImageColor.colormap.keys()): + expected = Image.new( + "RGB", (1, 1), color).convert("L").getpixel((0, 0)) + actual = Image.new("L", (1, 1), color).getpixel((0, 0)) + self.assertEqual(expected, actual) + + self.assertEqual((0, 0, 0), ImageColor.getcolor("black", "RGB")) + self.assertEqual((255, 255, 255), ImageColor.getcolor("white", "RGB")) + self.assertEqual( + (0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB")) + Image.new("RGB", (1, 1), "white") + + self.assertEqual((0, 0, 0, 255), ImageColor.getcolor("black", "RGBA")) + self.assertEqual( + (255, 255, 255, 255), ImageColor.getcolor("white", "RGBA")) + self.assertEqual( + (0, 255, 115, 33), + ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGBA")) + Image.new("RGBA", (1, 1), "white") + + self.assertEqual(0, ImageColor.getcolor("black", "L")) + self.assertEqual(255, ImageColor.getcolor("white", "L")) + self.assertEqual( + 162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) + Image.new("L", (1, 1), "white") + + self.assertEqual(0, ImageColor.getcolor("black", "1")) + self.assertEqual(255, ImageColor.getcolor("white", "1")) + # The following test is wrong, but is current behavior + # The correct result should be 255 due to the mode 1 + self.assertEqual( + 162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) + # Correct behavior + # self.assertEqual( + # 255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) + Image.new("1", (1, 1), "white") + + self.assertEqual((0, 255), ImageColor.getcolor("black", "LA")) + self.assertEqual((255, 255), ImageColor.getcolor("white", "LA")) + self.assertEqual( + (162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) + Image.new("LA", (1, 1), "white") + + +if __name__ == '__main__': + unittest.main() + +# End of file From e01e3e90d592e6c0865be77b66e4b32f7407f49a Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 5 Jun 2014 18:15:12 +0300 Subject: [PATCH 118/168] Convert more tests --- Tests/test_file_xpm.py | 14 ---- Tests/test_format_lab.py | 41 ----------- Tests/test_image_getbands.py | 15 ---- Tests/test_image_putalpha.py | 43 ------------ Tests/test_image_transform.py | 116 ------------------------------- test/test_file_xpm.py | 23 +++++++ test/test_format_lab.py | 48 +++++++++++++ test/test_image_getbands.py | 26 +++++++ test/test_image_putalpha.py | 52 ++++++++++++++ test/test_image_transform.py | 125 ++++++++++++++++++++++++++++++++++ 10 files changed, 274 insertions(+), 229 deletions(-) delete mode 100644 Tests/test_file_xpm.py delete mode 100644 Tests/test_format_lab.py delete mode 100644 Tests/test_image_getbands.py delete mode 100644 Tests/test_image_putalpha.py delete mode 100644 Tests/test_image_transform.py create mode 100644 test/test_file_xpm.py create mode 100644 test/test_format_lab.py create mode 100644 test/test_image_getbands.py create mode 100644 test/test_image_putalpha.py create mode 100644 test/test_image_transform.py diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py deleted file mode 100644 index 44135d028..000000000 --- a/Tests/test_file_xpm.py +++ /dev/null @@ -1,14 +0,0 @@ -from tester import * - -from PIL import Image - -# sample ppm stream -file = "Images/lena.xpm" -data = open(file, "rb").read() - -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "P") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "XPM") diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py deleted file mode 100644 index 371b06a0b..000000000 --- a/Tests/test_format_lab.py +++ /dev/null @@ -1,41 +0,0 @@ -from tester import * - -from PIL import Image - -def test_white(): - i = Image.open('Tests/images/lab.tif') - - bits = i.load() - - assert_equal(i.mode, 'LAB') - - assert_equal(i.getbands(), ('L','A', 'B')) - - k = i.getpixel((0,0)) - assert_equal(k, (255,128,128)) - - L = i.getdata(0) - a = i.getdata(1) - b = i.getdata(2) - - assert_equal(list(L), [255]*100) - assert_equal(list(a), [128]*100) - assert_equal(list(b), [128]*100) - - -def test_green(): - # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS - # == RGB: 0, 152, 117 - i = Image.open('Tests/images/lab-green.tif') - - k = i.getpixel((0,0)) - assert_equal(k, (128,28,128)) - - -def test_red(): - # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS - # == RGB: 255, 0, 124 - i = Image.open('Tests/images/lab-red.tif') - - k = i.getpixel((0,0)) - assert_equal(k, (128,228,128)) diff --git a/Tests/test_image_getbands.py b/Tests/test_image_getbands.py deleted file mode 100644 index e7f1ec5a9..000000000 --- a/Tests/test_image_getbands.py +++ /dev/null @@ -1,15 +0,0 @@ -from tester import * - -from PIL import Image - -def test_getbands(): - - assert_equal(Image.new("1", (1, 1)).getbands(), ("1",)) - assert_equal(Image.new("L", (1, 1)).getbands(), ("L",)) - assert_equal(Image.new("I", (1, 1)).getbands(), ("I",)) - assert_equal(Image.new("F", (1, 1)).getbands(), ("F",)) - assert_equal(Image.new("P", (1, 1)).getbands(), ("P",)) - assert_equal(Image.new("RGB", (1, 1)).getbands(), ("R", "G", "B")) - assert_equal(Image.new("RGBA", (1, 1)).getbands(), ("R", "G", "B", "A")) - assert_equal(Image.new("CMYK", (1, 1)).getbands(), ("C", "M", "Y", "K")) - assert_equal(Image.new("YCbCr", (1, 1)).getbands(), ("Y", "Cb", "Cr")) diff --git a/Tests/test_image_putalpha.py b/Tests/test_image_putalpha.py deleted file mode 100644 index b23f69834..000000000 --- a/Tests/test_image_putalpha.py +++ /dev/null @@ -1,43 +0,0 @@ -from tester import * - -from PIL import Image - -def test_interface(): - - im = Image.new("RGBA", (1, 1), (1, 2, 3, 0)) - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 0)) - - im = Image.new("RGBA", (1, 1), (1, 2, 3)) - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 255)) - - im.putalpha(Image.new("L", im.size, 4)) - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) - - im.putalpha(5) - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 5)) - -def test_promote(): - - im = Image.new("L", (1, 1), 1) - assert_equal(im.getpixel((0, 0)), 1) - - im.putalpha(2) - assert_equal(im.mode, 'LA') - assert_equal(im.getpixel((0, 0)), (1, 2)) - - im = Image.new("RGB", (1, 1), (1, 2, 3)) - assert_equal(im.getpixel((0, 0)), (1, 2, 3)) - - im.putalpha(4) - assert_equal(im.mode, 'RGBA') - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) - -def test_readonly(): - - im = Image.new("RGB", (1, 1), (1, 2, 3)) - im.readonly = 1 - - im.putalpha(4) - assert_false(im.readonly) - assert_equal(im.mode, 'RGBA') - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py deleted file mode 100644 index dd9b6fe5c..000000000 --- a/Tests/test_image_transform.py +++ /dev/null @@ -1,116 +0,0 @@ -from tester import * - -from PIL import Image - -def test_extent(): - im = lena('RGB') - (w,h) = im.size - transformed = im.transform(im.size, Image.EXTENT, - (0,0, - w//2,h//2), # ul -> lr - Image.BILINEAR) - - - scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) - - assert_image_similar(transformed, scaled, 10) # undone -- precision? - -def test_quad(): - # one simple quad transform, equivalent to scale & crop upper left quad - im = lena('RGB') - (w,h) = im.size - transformed = im.transform(im.size, Image.QUAD, - (0,0,0,h//2, - w//2,h//2,w//2,0), # ul -> ccw around quad - Image.BILINEAR) - - scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) - - assert_image_equal(transformed, scaled) - -def test_mesh(): - # this should be a checkerboard of halfsized lenas in ul, lr - im = lena('RGBA') - (w,h) = im.size - transformed = im.transform(im.size, Image.MESH, - [((0,0,w//2,h//2), # box - (0,0,0,h, - w,h,w,0)), # ul -> ccw around quad - ((w//2,h//2,w,h), # box - (0,0,0,h, - w,h,w,0))], # ul -> ccw around quad - Image.BILINEAR) - - #transformed.save('transformed.png') - - scaled = im.resize((w//2, h//2), Image.BILINEAR) - - checker = Image.new('RGBA', im.size) - checker.paste(scaled, (0,0)) - checker.paste(scaled, (w//2,h//2)) - - assert_image_equal(transformed, checker) - - # now, check to see that the extra area is (0,0,0,0) - blank = Image.new('RGBA', (w//2,h//2), (0,0,0,0)) - - assert_image_equal(blank, transformed.crop((w//2,0,w,h//2))) - assert_image_equal(blank, transformed.crop((0,h//2,w//2,h))) - -def _test_alpha_premult(op): - # create image with half white, half black, with the black half transparent. - # do op, - # there should be no darkness in the white section. - im = Image.new('RGBA', (10,10), (0,0,0,0)); - im2 = Image.new('RGBA', (5,10), (255,255,255,255)); - im.paste(im2, (0,0)) - - im = op(im, (40,10)) - im_background = Image.new('RGB', (40,10), (255,255,255)) - im_background.paste(im, (0,0), im) - - hist = im_background.histogram() - assert_equal(40*10, hist[-1]) - - -def test_alpha_premult_resize(): - - def op (im, sz): - return im.resize(sz, Image.LINEAR) - - _test_alpha_premult(op) - -def test_alpha_premult_transform(): - - def op(im, sz): - (w,h) = im.size - return im.transform(sz, Image.EXTENT, - (0,0, - w,h), - Image.BILINEAR) - - _test_alpha_premult(op) - - -def test_blank_fill(): - # attempting to hit - # https://github.com/python-pillow/Pillow/issues/254 reported - # - # issue is that transforms with transparent overflow area - # contained junk from previous images, especially on systems with - # constrained memory. So, attempt to fill up memory with a - # pattern, free it, and then run the mesh test again. Using a 1Mp - # image with 4 bands, for 4 megs of data allocated, x 64. OMM (64 - # bit 12.04 VM with 512 megs available, this fails with Pillow < - # a0eaf06cc5f62a6fb6de556989ac1014ff3348ea - # - # Running by default, but I'd totally understand not doing it in - # the future - - foo = [Image.new('RGBA',(1024,1024), (a,a,a,a)) - for a in range(1,65)] - - # Yeah. Watch some JIT optimize this out. - foo = None - - test_mesh() diff --git a/test/test_file_xpm.py b/test/test_file_xpm.py new file mode 100644 index 000000000..ecbb4137a --- /dev/null +++ b/test/test_file_xpm.py @@ -0,0 +1,23 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + +# sample ppm stream +file = "Images/lena.xpm" +data = open(file, "rb").read() + + +class TestFileXpm(PillowTestCase): + + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "P") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "XPM") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_format_lab.py b/test/test_format_lab.py new file mode 100644 index 000000000..53468db5f --- /dev/null +++ b/test/test_format_lab.py @@ -0,0 +1,48 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + + +class TestFormatLab(PillowTestCase): + + def test_white(self): + i = Image.open('Tests/images/lab.tif') + + i.load() + + self.assertEqual(i.mode, 'LAB') + + self.assertEqual(i.getbands(), ('L', 'A', 'B')) + + k = i.getpixel((0, 0)) + self.assertEqual(k, (255, 128, 128)) + + L = i.getdata(0) + a = i.getdata(1) + b = i.getdata(2) + + self.assertEqual(list(L), [255]*100) + self.assertEqual(list(a), [128]*100) + self.assertEqual(list(b), [128]*100) + + def test_green(self): + # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS + # == RGB: 0, 152, 117 + i = Image.open('Tests/images/lab-green.tif') + + k = i.getpixel((0, 0)) + self.assertEqual(k, (128, 28, 128)) + + def test_red(self): + # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS + # == RGB: 255, 0, 124 + i = Image.open('Tests/images/lab-red.tif') + + k = i.getpixel((0, 0)) + self.assertEqual(k, (128, 228, 128)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_getbands.py b/test/test_image_getbands.py new file mode 100644 index 000000000..e803abb02 --- /dev/null +++ b/test/test_image_getbands.py @@ -0,0 +1,26 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + + +class TestImageGetBands(PillowTestCase): + + def test_getbands(self): + self.assertEqual(Image.new("1", (1, 1)).getbands(), ("1",)) + self.assertEqual(Image.new("L", (1, 1)).getbands(), ("L",)) + self.assertEqual(Image.new("I", (1, 1)).getbands(), ("I",)) + self.assertEqual(Image.new("F", (1, 1)).getbands(), ("F",)) + self.assertEqual(Image.new("P", (1, 1)).getbands(), ("P",)) + self.assertEqual(Image.new("RGB", (1, 1)).getbands(), ("R", "G", "B")) + self.assertEqual( + Image.new("RGBA", (1, 1)).getbands(), ("R", "G", "B", "A")) + self.assertEqual( + Image.new("CMYK", (1, 1)).getbands(), ("C", "M", "Y", "K")) + self.assertEqual( + Image.new("YCbCr", (1, 1)).getbands(), ("Y", "Cb", "Cr")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_putalpha.py b/test/test_image_putalpha.py new file mode 100644 index 000000000..85c7ac262 --- /dev/null +++ b/test/test_image_putalpha.py @@ -0,0 +1,52 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + + +class TestImagePutAlpha(PillowTestCase): + + def test_interface(self): + + im = Image.new("RGBA", (1, 1), (1, 2, 3, 0)) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 0)) + + im = Image.new("RGBA", (1, 1), (1, 2, 3)) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 255)) + + im.putalpha(Image.new("L", im.size, 4)) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) + + im.putalpha(5) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 5)) + + def test_promote(self): + + im = Image.new("L", (1, 1), 1) + self.assertEqual(im.getpixel((0, 0)), 1) + + im.putalpha(2) + self.assertEqual(im.mode, 'LA') + self.assertEqual(im.getpixel((0, 0)), (1, 2)) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3)) + + im.putalpha(4) + self.assertEqual(im.mode, 'RGBA') + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) + + def test_readonly(self): + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.readonly = 1 + + im.putalpha(4) + self.assertFalse(im.readonly) + self.assertEqual(im.mode, 'RGBA') + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_transform.py b/test/test_image_transform.py new file mode 100644 index 000000000..6ab186c12 --- /dev/null +++ b/test/test_image_transform.py @@ -0,0 +1,125 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + + +class TestImageTransform(PillowTestCase): + + def test_extent(self): + im = lena('RGB') + (w, h) = im.size + transformed = im.transform(im.size, Image.EXTENT, + (0, 0, + w//2, h//2), # ul -> lr + Image.BILINEAR) + + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h)) + + # undone -- precision? + self.assert_image_similar(transformed, scaled, 10) + + def test_quad(self): + # one simple quad transform, equivalent to scale & crop upper left quad + im = lena('RGB') + (w, h) = im.size + transformed = im.transform(im.size, Image.QUAD, + (0, 0, 0, h//2, + # ul -> ccw around quad: + w//2, h//2, w//2, 0), + Image.BILINEAR) + + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h)) + + self.assert_image_equal(transformed, scaled) + + def test_mesh(self): + # this should be a checkerboard of halfsized lenas in ul, lr + im = lena('RGBA') + (w, h) = im.size + transformed = im.transform(im.size, Image.MESH, + [((0, 0, w//2, h//2), # box + (0, 0, 0, h, + w, h, w, 0)), # ul -> ccw around quad + ((w//2, h//2, w, h), # box + (0, 0, 0, h, + w, h, w, 0))], # ul -> ccw around quad + Image.BILINEAR) + + # transformed.save('transformed.png') + + scaled = im.resize((w//2, h//2), Image.BILINEAR) + + checker = Image.new('RGBA', im.size) + checker.paste(scaled, (0, 0)) + checker.paste(scaled, (w//2, h//2)) + + self.assert_image_equal(transformed, checker) + + # now, check to see that the extra area is (0, 0, 0, 0) + blank = Image.new('RGBA', (w//2, h//2), (0, 0, 0, 0)) + + self.assert_image_equal(blank, transformed.crop((w//2, 0, w, h//2))) + self.assert_image_equal(blank, transformed.crop((0, h//2, w//2, h))) + + def _test_alpha_premult(self, op): + # create image with half white, half black, + # with the black half transparent. + # do op, + # there should be no darkness in the white section. + im = Image.new('RGBA', (10, 10), (0, 0, 0, 0)) + im2 = Image.new('RGBA', (5, 10), (255, 255, 255, 255)) + im.paste(im2, (0, 0)) + + im = op(im, (40, 10)) + im_background = Image.new('RGB', (40, 10), (255, 255, 255)) + im_background.paste(im, (0, 0), im) + + hist = im_background.histogram() + self.assertEqual(40*10, hist[-1]) + + def test_alpha_premult_resize(self): + + def op(im, sz): + return im.resize(sz, Image.LINEAR) + + self._test_alpha_premult(op) + + def test_alpha_premult_transform(self): + + def op(im, sz): + (w, h) = im.size + return im.transform(sz, Image.EXTENT, + (0, 0, + w, h), + Image.BILINEAR) + + self._test_alpha_premult(op) + + def test_blank_fill(self): + # attempting to hit + # https://github.com/python-pillow/Pillow/issues/254 reported + # + # issue is that transforms with transparent overflow area + # contained junk from previous images, especially on systems with + # constrained memory. So, attempt to fill up memory with a + # pattern, free it, and then run the mesh test again. Using a 1Mp + # image with 4 bands, for 4 megs of data allocated, x 64. OMM (64 + # bit 12.04 VM with 512 megs available, this fails with Pillow < + # a0eaf06cc5f62a6fb6de556989ac1014ff3348ea + # + # Running by default, but I'd totally understand not doing it in + # the future + + foo = [Image.new('RGBA', (1024, 1024), (a, a, a, a)) + for a in range(1, 65)] + + # Yeah. Watch some JIT optimize this out. + foo = None + + self.test_mesh() + + +if __name__ == '__main__': + unittest.main() + +# End of file From e87ae09328cde2da7193f696e10ae4480ff3c7f3 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 5 Jun 2014 21:31:26 +0300 Subject: [PATCH 119/168] Delete test test [CI skip] --- test/mini.py | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 test/mini.py diff --git a/test/mini.py b/test/mini.py deleted file mode 100644 index c9f1665ab..000000000 --- a/test/mini.py +++ /dev/null @@ -1,5 +0,0 @@ -from test_image_putpixel import TestImagePutPixel as put -from test_image_getpixel import TestImageGetPixel as get - -dir(get) -get.test_basic() \ No newline at end of file From 0eb92eccd7d4528093b21f6a9b3f3396da16fede Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 5 Jun 2014 22:10:12 +0300 Subject: [PATCH 120/168] Skip test_floodfill_border() on PyPy Otherwise it sometimes, but not always, causes an error: RPython traceback: File "rpython_jit_metainterp_compile.c", line 20472, in send_loop_to_backend File "rpython_jit_backend_x86_assembler.c", line 1818, in Assembler386_assemble_loop File "rpython_jit_backend_x86_regalloc.c", line 293, in RegAlloc_prepare_loop File "rpython_jit_backend_x86_regalloc.c", line 909, in RegAlloc__prepare File "rpython_jit_backend_llsupport_regalloc.c", line 4706, in compute_vars_longevity Fatal RPython error: AssertionError /home/travis/build.sh: line 236: 7300 Aborted --- test/test_imagedraw.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_imagedraw.py b/test/test_imagedraw.py index 44403e50c..98876296f 100644 --- a/test/test_imagedraw.py +++ b/test/test_imagedraw.py @@ -4,6 +4,8 @@ from PIL import Image from PIL import ImageColor from PIL import ImageDraw +import sys + # Image size w, h = 100, 100 @@ -225,7 +227,11 @@ class TestImageDraw(PillowTestCase): self.assert_image_equal( im, Image.open("Tests/images/imagedraw_floodfill.png")) + @unittest.skipIf(hasattr(sys, 'pypy_version_info'), + "Causes fatal RPython error on PyPy") def test_floodfill_border(self): + # floodfill() is experimental + # Arrange im = Image.new("RGB", (w, h)) draw = ImageDraw.Draw(im) From 995f3677559f6387ed43a3b224856de3a732fe14 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 08:02:18 +0300 Subject: [PATCH 121/168] Skip test_floodfill_border() on PyPy to avoid fatal error --- Tests/test_imagedraw.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index c47638a05..7b682020e 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -246,6 +246,11 @@ def test_floodfill(): def test_floodfill_border(): + # floodfill() is experimental + if hasattr(sys, 'pypy_version_info'): + # Causes fatal RPython error on PyPy + skip() + # Arrange im = Image.new("RGB", (w, h)) draw = ImageDraw.Draw(im) From 4ab5b6c583f5c21ae065cbacb66bc6b25f92005b Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 10:48:28 +0300 Subject: [PATCH 122/168] Comment out lena() caching --- test/helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/helper.py b/test/helper.py index e6684b845..455ff9673 100644 --- a/test/helper.py +++ b/test/helper.py @@ -169,9 +169,9 @@ def tostring(im, format, **options): return out.getvalue() -def lena(mode="RGB", cache={}): +def lena(mode="RGB"): # , cache={}): from PIL import Image - im = cache.get(mode) + # im = cache.get(mode) if im is None: if mode == "RGB": im = Image.open("Images/lena.ppm") @@ -181,7 +181,7 @@ def lena(mode="RGB", cache={}): im = lena("I").convert(mode) else: im = lena("RGB").convert(mode) - cache[mode] = im + # cache[mode] = im return im From 40ada60ceceedb67a1e91121cb89f49862f9bf9f Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 10:55:04 +0300 Subject: [PATCH 123/168] Comment out lena() caching --- test/helper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/helper.py b/test/helper.py index 455ff9673..cfe7b86c7 100644 --- a/test/helper.py +++ b/test/helper.py @@ -172,6 +172,7 @@ def tostring(im, format, **options): def lena(mode="RGB"): # , cache={}): from PIL import Image # im = cache.get(mode) + im = None if im is None: if mode == "RGB": im = Image.open("Images/lena.ppm") From e2e361141c1d4f90542a198e427de2450321b407 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 14:04:26 +0300 Subject: [PATCH 124/168] Return duplicate in lena() --- .travis.yml | 12 ++++++------ test/helper.py | 11 ++++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 48206a64d..ef5094a68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,14 +29,14 @@ script: - python setup.py build_ext --inplace # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then nosetests test/; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time python selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests test/; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time python Tests/run.py; fi # Cover the others - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* -m nose test/; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose test/; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time python Tests/run.py --coverage; fi after_success: diff --git a/test/helper.py b/test/helper.py index cfe7b86c7..9fe66a721 100644 --- a/test/helper.py +++ b/test/helper.py @@ -169,10 +169,9 @@ def tostring(im, format, **options): return out.getvalue() -def lena(mode="RGB"): # , cache={}): +def lena(mode="RGB"), cache={}): from PIL import Image - # im = cache.get(mode) - im = None + im = cache.get(mode) if im is None: if mode == "RGB": im = Image.open("Images/lena.ppm") @@ -182,8 +181,10 @@ def lena(mode="RGB"): # , cache={}): im = lena("I").convert(mode) else: im = lena("RGB").convert(mode) - # cache[mode] = im - return im + cache[mode] = im + import copy + duplicate = copy.copy(im) + return duplicate # def assert_image_completely_equal(a, b, msg=None): From c45f20119c2ba40d4802f984e8d54afacd160a2b Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 14:53:33 +0300 Subject: [PATCH 125/168] Fix syntax error --- test/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helper.py b/test/helper.py index 9fe66a721..f97faf4d1 100644 --- a/test/helper.py +++ b/test/helper.py @@ -169,7 +169,7 @@ def tostring(im, format, **options): return out.getvalue() -def lena(mode="RGB"), cache={}): +def lena(mode="RGB", cache={}): from PIL import Image im = cache.get(mode) if im is None: From 3ff2ea4883b32303c6baacbc5df06d3fc76c4a1a Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 15:19:28 +0300 Subject: [PATCH 126/168] Disable lena() caching for now, convert more tests --- Tests/test_file_icns.py | 66 -------------------------------- Tests/test_image_resize.py | 12 ------ Tests/test_imagefileio.py | 24 ------------ Tests/test_imagetransform.py | 18 --------- Tests/test_locale.py | 31 --------------- test/helper.py | 9 ++--- test/test_file_icns.py | 74 ++++++++++++++++++++++++++++++++++++ test/test_image_resize.py | 19 +++++++++ test/test_imagefileio.py | 31 +++++++++++++++ test/test_imagetransform.py | 27 +++++++++++++ test/test_locale.py | 39 +++++++++++++++++++ 11 files changed, 194 insertions(+), 156 deletions(-) delete mode 100644 Tests/test_file_icns.py delete mode 100644 Tests/test_image_resize.py delete mode 100644 Tests/test_imagefileio.py delete mode 100644 Tests/test_imagetransform.py delete mode 100644 Tests/test_locale.py create mode 100644 test/test_file_icns.py create mode 100644 test/test_image_resize.py create mode 100644 test/test_imagefileio.py create mode 100644 test/test_imagetransform.py create mode 100644 test/test_locale.py diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py deleted file mode 100644 index 3e31f8879..000000000 --- a/Tests/test_file_icns.py +++ /dev/null @@ -1,66 +0,0 @@ -from tester import * - -from PIL import Image - -# sample icon file -file = "Images/pillow.icns" -data = open(file, "rb").read() - -enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') - -def test_sanity(): - # Loading this icon by default should result in the largest size - # (512x512@2x) being loaded - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGBA") - assert_equal(im.size, (1024, 1024)) - assert_equal(im.format, "ICNS") - -def test_sizes(): - # Check that we can load all of the sizes, and that the final pixel - # dimensions are as expected - im = Image.open(file) - for w,h,r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open(file) - im2.size = (w, h, r) - im2.load() - assert_equal(im2.mode, 'RGBA') - assert_equal(im2.size, (wr, hr)) - -def test_older_icon(): - # This icon was made with Icon Composer rather than iconutil; it still - # uses PNG rather than JP2, however (since it was made on 10.9). - im = Image.open('Tests/images/pillow2.icns') - for w,h,r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open('Tests/images/pillow2.icns') - im2.size = (w, h, r) - im2.load() - assert_equal(im2.mode, 'RGBA') - assert_equal(im2.size, (wr, hr)) - -def test_jp2_icon(): - # This icon was made by using Uli Kusterer's oldiconutil to replace - # the PNG images with JPEG 2000 ones. The advantage of doing this is - # that OS X 10.5 supports JPEG 2000 but not PNG; some commercial - # software therefore does just this. - - # (oldiconutil is here: https://github.com/uliwitness/oldiconutil) - - if not enable_jpeg2k: - return - - im = Image.open('Tests/images/pillow3.icns') - for w,h,r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open('Tests/images/pillow3.icns') - im2.size = (w, h, r) - im2.load() - assert_equal(im2.mode, 'RGBA') - assert_equal(im2.size, (wr, hr)) - diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py deleted file mode 100644 index 4e228a396..000000000 --- a/Tests/test_image_resize.py +++ /dev/null @@ -1,12 +0,0 @@ -from tester import * - -from PIL import Image - -def test_resize(): - def resize(mode, size): - out = lena(mode).resize(size) - assert_equal(out.mode, mode) - assert_equal(out.size, size) - for mode in "1", "P", "L", "RGB", "I", "F": - yield_test(resize, mode, (100, 100)) - yield_test(resize, mode, (200, 200)) diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py deleted file mode 100644 index c63f07bb0..000000000 --- a/Tests/test_imagefileio.py +++ /dev/null @@ -1,24 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageFileIO - -def test_fileio(): - - class DumbFile: - def __init__(self, data): - self.data = data - def read(self, bytes=None): - assert_equal(bytes, None) - return self.data - def close(self): - pass - - im1 = lena() - - io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) - - im2 = Image.open(io) - assert_image_equal(im1, im2) - - diff --git a/Tests/test_imagetransform.py b/Tests/test_imagetransform.py deleted file mode 100644 index 884e6bb1c..000000000 --- a/Tests/test_imagetransform.py +++ /dev/null @@ -1,18 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageTransform - -im = Image.new("L", (100, 100)) - -seq = tuple(range(10)) - -def test_sanity(): - transform = ImageTransform.AffineTransform(seq[:6]) - assert_no_exception(lambda: im.transform((100, 100), transform)) - transform = ImageTransform.ExtentTransform(seq[:4]) - assert_no_exception(lambda: im.transform((100, 100), transform)) - transform = ImageTransform.QuadTransform(seq[:8]) - assert_no_exception(lambda: im.transform((100, 100), transform)) - transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) - assert_no_exception(lambda: im.transform((100, 100), transform)) diff --git a/Tests/test_locale.py b/Tests/test_locale.py deleted file mode 100644 index 6b2b95201..000000000 --- a/Tests/test_locale.py +++ /dev/null @@ -1,31 +0,0 @@ -from tester import * -from PIL import Image - -import locale - -# ref https://github.com/python-pillow/Pillow/issues/272 -## on windows, in polish locale: - -## import locale -## print locale.setlocale(locale.LC_ALL, 'polish') -## import string -## print len(string.whitespace) -## print ord(string.whitespace[6]) - -## Polish_Poland.1250 -## 7 -## 160 - -# one of string.whitespace is not freely convertable into ascii. - -path = "Images/lena.jpg" - -def test_sanity(): - assert_no_exception(lambda: Image.open(path)) - try: - locale.setlocale(locale.LC_ALL, "polish") - except: - skip('polish locale not available') - import string - assert_no_exception(lambda: Image.open(path)) - diff --git a/test/helper.py b/test/helper.py index f97faf4d1..ebe651120 100644 --- a/test/helper.py +++ b/test/helper.py @@ -171,7 +171,8 @@ def tostring(im, format, **options): def lena(mode="RGB", cache={}): from PIL import Image - im = cache.get(mode) + im = None + # im = cache.get(mode) if im is None: if mode == "RGB": im = Image.open("Images/lena.ppm") @@ -181,10 +182,8 @@ def lena(mode="RGB", cache={}): im = lena("I").convert(mode) else: im = lena("RGB").convert(mode) - cache[mode] = im - import copy - duplicate = copy.copy(im) - return duplicate + # cache[mode] = im + return im # def assert_image_completely_equal(a, b, msg=None): diff --git a/test/test_file_icns.py b/test/test_file_icns.py new file mode 100644 index 000000000..9d838620b --- /dev/null +++ b/test/test_file_icns.py @@ -0,0 +1,74 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + +# sample icon file +file = "Images/pillow.icns" +data = open(file, "rb").read() + +enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') + + +class TestFileIcns(PillowTestCase): + + def test_sanity(self): + # Loading this icon by default should result in the largest size + # (512x512@2x) being loaded + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (1024, 1024)) + self.assertEqual(im.format, "ICNS") + + def test_sizes(self): + # Check that we can load all of the sizes, and that the final pixel + # dimensions are as expected + im = Image.open(file) + for w, h, r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open(file) + im2.size = (w, h, r) + im2.load() + self.assertEqual(im2.mode, 'RGBA') + self.assertEqual(im2.size, (wr, hr)) + + def test_older_icon(self): + # This icon was made with Icon Composer rather than iconutil; it still + # uses PNG rather than JP2, however (since it was made on 10.9). + im = Image.open('Tests/images/pillow2.icns') + for w, h, r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open('Tests/images/pillow2.icns') + im2.size = (w, h, r) + im2.load() + self.assertEqual(im2.mode, 'RGBA') + self.assertEqual(im2.size, (wr, hr)) + + def test_jp2_icon(self): + # This icon was made by using Uli Kusterer's oldiconutil to replace + # the PNG images with JPEG 2000 ones. The advantage of doing this is + # that OS X 10.5 supports JPEG 2000 but not PNG; some commercial + # software therefore does just this. + + # (oldiconutil is here: https://github.com/uliwitness/oldiconutil) + + if not enable_jpeg2k: + return + + im = Image.open('Tests/images/pillow3.icns') + for w, h, r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open('Tests/images/pillow3.icns') + im2.size = (w, h, r) + im2.load() + self.assertEqual(im2.mode, 'RGBA') + self.assertEqual(im2.size, (wr, hr)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_resize.py b/test/test_image_resize.py new file mode 100644 index 000000000..6c9932e45 --- /dev/null +++ b/test/test_image_resize.py @@ -0,0 +1,19 @@ +from helper import unittest, PillowTestCase, lena + + +class TestImageResize(PillowTestCase): + + def test_resize(self): + def resize(mode, size): + out = lena(mode).resize(size) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, size) + for mode in "1", "P", "L", "RGB", "I", "F": + resize(mode, (100, 100)) + resize(mode, (200, 200)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagefileio.py b/test/test_imagefileio.py new file mode 100644 index 000000000..73f660361 --- /dev/null +++ b/test/test_imagefileio.py @@ -0,0 +1,31 @@ +from helper import unittest, PillowTestCase, lena, tostring + +from PIL import Image +from PIL import ImageFileIO + + +class TestImageFileIo(PillowTestCase): + + def test_fileio(self): + + class DumbFile: + def __init__(self, data): + self.data = data + def read(self, bytes=None): + self.assertEqual(bytes, None) + return self.data + def close(self): + pass + + im1 = lena() + + io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) + + im2 = Image.open(io) + self.assert_image_equal(im1, im2) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagetransform.py b/test/test_imagetransform.py new file mode 100644 index 000000000..f5741df32 --- /dev/null +++ b/test/test_imagetransform.py @@ -0,0 +1,27 @@ +from helper import unittest, PillowTestCase + +from PIL import Image +from PIL import ImageTransform + + +class TestImageTransform(PillowTestCase): + + def test_sanity(self): + im = Image.new("L", (100, 100)) + + seq = tuple(range(10)) + + transform = ImageTransform.AffineTransform(seq[:6]) + im.transform((100, 100), transform) + transform = ImageTransform.ExtentTransform(seq[:4]) + im.transform((100, 100), transform) + transform = ImageTransform.QuadTransform(seq[:8]) + im.transform((100, 100), transform) + transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) + im.transform((100, 100), transform) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_locale.py b/test/test_locale.py new file mode 100644 index 000000000..599e46266 --- /dev/null +++ b/test/test_locale.py @@ -0,0 +1,39 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + +import locale + +# ref https://github.com/python-pillow/Pillow/issues/272 +# on windows, in polish locale: + +# import locale +# print locale.setlocale(locale.LC_ALL, 'polish') +# import string +# print len(string.whitespace) +# print ord(string.whitespace[6]) + +# Polish_Poland.1250 +# 7 +# 160 + +# one of string.whitespace is not freely convertable into ascii. + +path = "Images/lena.jpg" + + +class TestLocale(PillowTestCase): + + def test_sanity(self): + Image.open(path) + try: + locale.setlocale(locale.LC_ALL, "polish") + except: + unittest.skip('Polish locale not available') + Image.open(path) + + +if __name__ == '__main__': + unittest.main() + +# End of file From cbb2f9dce941698b448c579034687c7f57145970 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 15:26:48 +0300 Subject: [PATCH 127/168] Fix test/test_imagefileio.py --- Tests/test_image_paste.py | 5 ----- Tests/test_image_save.py | 5 ----- Tests/test_image_seek.py | 5 ----- Tests/test_image_show.py | 5 ----- Tests/test_image_tell.py | 5 ----- Tests/test_image_verify.py | 5 ----- test/test_imagefileio.py | 4 +++- 7 files changed, 3 insertions(+), 31 deletions(-) delete mode 100644 Tests/test_image_paste.py delete mode 100644 Tests/test_image_save.py delete mode 100644 Tests/test_image_seek.py delete mode 100644 Tests/test_image_show.py delete mode 100644 Tests/test_image_tell.py delete mode 100644 Tests/test_image_verify.py diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_paste.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_save.py b/Tests/test_image_save.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_save.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_seek.py b/Tests/test_image_seek.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_seek.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_show.py b/Tests/test_image_show.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_show.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_tell.py b/Tests/test_image_tell.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_tell.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_verify.py b/Tests/test_image_verify.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_verify.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/test/test_imagefileio.py b/test/test_imagefileio.py index 73f660361..32ee0bc5e 100644 --- a/test/test_imagefileio.py +++ b/test/test_imagefileio.py @@ -11,9 +11,11 @@ class TestImageFileIo(PillowTestCase): class DumbFile: def __init__(self, data): self.data = data + def read(self, bytes=None): - self.assertEqual(bytes, None) + assert(bytes is None) return self.data + def close(self): pass From c4d3898006b5d5c5af90312e15ba558e1bcd3a01 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 16:55:00 +0300 Subject: [PATCH 128/168] Convert more tests --- Tests/test_file_ico.py | 14 --- Tests/test_font_bdf.py | 13 --- Tests/test_image_offset.py | 16 --- Tests/test_imagecms.py | 203 ----------------------------------- Tests/test_imagemode.py | 23 ---- Tests/test_imageshow.py | 6 -- Tests/test_imagetk.py | 9 -- Tests/test_imagewin.py | 6 -- test/helper.py | 5 +- test/test_file_ico.py | 23 ++++ test/test_font_bdf.py | 22 ++++ test/test_image_offset.py | 25 +++++ test/test_imagecms.py | 214 +++++++++++++++++++++++++++++++++++++ test/test_imagemode.py | 32 ++++++ test/test_imageshow.py | 18 ++++ test/test_imagetk.py | 17 +++ test/test_imagewin.py | 18 ++++ 17 files changed, 372 insertions(+), 292 deletions(-) delete mode 100644 Tests/test_file_ico.py delete mode 100644 Tests/test_font_bdf.py delete mode 100644 Tests/test_image_offset.py delete mode 100644 Tests/test_imagecms.py delete mode 100644 Tests/test_imagemode.py delete mode 100644 Tests/test_imageshow.py delete mode 100644 Tests/test_imagetk.py delete mode 100644 Tests/test_imagewin.py create mode 100644 test/test_file_ico.py create mode 100644 test/test_font_bdf.py create mode 100644 test/test_image_offset.py create mode 100644 test/test_imagecms.py create mode 100644 test/test_imagemode.py create mode 100644 test/test_imageshow.py create mode 100644 test/test_imagetk.py create mode 100644 test/test_imagewin.py diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py deleted file mode 100644 index e0db34acc..000000000 --- a/Tests/test_file_ico.py +++ /dev/null @@ -1,14 +0,0 @@ -from tester import * - -from PIL import Image - -# sample ppm stream -file = "Images/lena.ico" -data = open(file, "rb").read() - -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGBA") - assert_equal(im.size, (16, 16)) - assert_equal(im.format, "ICO") diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py deleted file mode 100644 index 366bb4468..000000000 --- a/Tests/test_font_bdf.py +++ /dev/null @@ -1,13 +0,0 @@ -from tester import * - -from PIL import Image, FontFile, BdfFontFile - -filename = "Images/courB08.bdf" - -def test_sanity(): - - file = open(filename, "rb") - font = BdfFontFile.BdfFontFile(file) - - assert_true(isinstance(font, FontFile.FontFile)) - assert_equal(len([_f for _f in font.glyph if _f]), 190) diff --git a/Tests/test_image_offset.py b/Tests/test_image_offset.py deleted file mode 100644 index f6356907a..000000000 --- a/Tests/test_image_offset.py +++ /dev/null @@ -1,16 +0,0 @@ -from tester import * - -from PIL import Image - -def test_offset(): - - im1 = lena() - - im2 = assert_warning(DeprecationWarning, lambda: im1.offset(10)) - assert_equal(im1.getpixel((0, 0)), im2.getpixel((10, 10))) - - im2 = assert_warning(DeprecationWarning, lambda: im1.offset(10, 20)) - assert_equal(im1.getpixel((0, 0)), im2.getpixel((10, 20))) - - im2 = assert_warning(DeprecationWarning, lambda: im1.offset(20, 20)) - assert_equal(im1.getpixel((0, 0)), im2.getpixel((20, 20))) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py deleted file mode 100644 index f52101eb1..000000000 --- a/Tests/test_imagecms.py +++ /dev/null @@ -1,203 +0,0 @@ -from tester import * - -from PIL import Image -try: - from PIL import ImageCms - ImageCms.core.profile_open -except ImportError: - skip() - -SRGB = "Tests/icc/sRGB.icm" - - -def test_sanity(): - - # basic smoke test. - # this mostly follows the cms_test outline. - - v = ImageCms.versions() # should return four strings - assert_equal(v[0], '1.0.0 pil') - assert_equal(list(map(type, v)), [str, str, str, str]) - - # internal version number - assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") - - i = ImageCms.profileToProfile(lena(), SRGB, SRGB) - assert_image(i, "RGB", (128, 128)) - - i = lena() - ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) - assert_image(i, "RGB", (128, 128)) - - t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") - i = ImageCms.applyTransform(lena(), t) - assert_image(i, "RGB", (128, 128)) - - i = lena() - t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") - ImageCms.applyTransform(lena(), t, inPlace=True) - assert_image(i, "RGB", (128, 128)) - - p = ImageCms.createProfile("sRGB") - o = ImageCms.getOpenProfile(SRGB) - t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") - i = ImageCms.applyTransform(lena(), t) - assert_image(i, "RGB", (128, 128)) - - t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") - assert_equal(t.inputMode, "RGB") - assert_equal(t.outputMode, "RGB") - i = ImageCms.applyTransform(lena(), t) - assert_image(i, "RGB", (128, 128)) - - # test PointTransform convenience API - lena().point(t) - - -def test_name(): - # get profile information for file - assert_equal(ImageCms.getProfileName(SRGB).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') - - -def test_info(): - assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), - ['sRGB IEC61966-2.1', '', - 'Copyright (c) 1998 Hewlett-Packard Company', '']) - - -def test_copyright(): - assert_equal(ImageCms.getProfileCopyright(SRGB).strip(), - 'Copyright (c) 1998 Hewlett-Packard Company') - - -def test_manufacturer(): - assert_equal(ImageCms.getProfileManufacturer(SRGB).strip(), - 'IEC http://www.iec.ch') - - -def test_model(): - assert_equal(ImageCms.getProfileModel(SRGB).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') - - -def test_description(): - assert_equal(ImageCms.getProfileDescription(SRGB).strip(), - 'sRGB IEC61966-2.1') - - -def test_intent(): - assert_equal(ImageCms.getDefaultIntent(SRGB), 0) - assert_equal(ImageCms.isIntentSupported( - SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) - - -def test_profile_object(): - # same, using profile object - p = ImageCms.createProfile("sRGB") -# assert_equal(ImageCms.getProfileName(p).strip(), -# 'sRGB built-in - (lcms internal)') -# assert_equal(ImageCms.getProfileInfo(p).splitlines(), -# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) - assert_equal(ImageCms.getDefaultIntent(p), 0) - assert_equal(ImageCms.isIntentSupported( - p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) - - -def test_extensions(): - # extensions - i = Image.open("Tests/images/rgb.jpg") - p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) - assert_equal(ImageCms.getProfileName(p).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') - - -def test_exceptions(): - # the procedural pyCMS API uses PyCMSError for all sorts of errors - assert_exception( - ImageCms.PyCMSError, - lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) - assert_exception( - ImageCms.PyCMSError, - lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) - assert_exception( - ImageCms.PyCMSError, - lambda: ImageCms.getProfileName(None)) - assert_exception( - ImageCms.PyCMSError, - lambda: ImageCms.isIntentSupported(SRGB, None, None)) - - -def test_display_profile(): - # try fetching the profile for the current display device - assert_no_exception(lambda: ImageCms.get_display_profile()) - - -def test_lab_color_profile(): - ImageCms.createProfile("LAB", 5000) - ImageCms.createProfile("LAB", 6500) - - -def test_simple_lab(): - i = Image.new('RGB', (10, 10), (128, 128, 128)) - - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - - i_lab = ImageCms.applyTransform(i, t) - - assert_equal(i_lab.mode, 'LAB') - - k = i_lab.getpixel((0, 0)) - assert_equal(k, (137, 128, 128)) # not a linear luminance map. so L != 128 - - L = i_lab.getdata(0) - a = i_lab.getdata(1) - b = i_lab.getdata(2) - - assert_equal(list(L), [137] * 100) - assert_equal(list(a), [128] * 100) - assert_equal(list(b), [128] * 100) - - -def test_lab_color(): - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - # Need to add a type mapping for some PIL type to TYPE_Lab_8 in - # findLCMSType, and have that mapping work back to a PIL mode (likely RGB). - i = ImageCms.applyTransform(lena(), t) - assert_image(i, "LAB", (128, 128)) - - # i.save('temp.lab.tif') # visually verified vs PS. - - target = Image.open('Tests/images/lena.Lab.tif') - - assert_image_similar(i, target, 30) - - -def test_lab_srgb(): - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") - - img = Image.open('Tests/images/lena.Lab.tif') - - img_srgb = ImageCms.applyTransform(img, t) - - # img_srgb.save('temp.srgb.tif') # visually verified vs ps. - - assert_image_similar(lena(), img_srgb, 30) - - -def test_lab_roundtrip(): - # check to see if we're at least internally consistent. - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - - t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") - - i = ImageCms.applyTransform(lena(), t) - out = ImageCms.applyTransform(i, t2) - - assert_image_similar(lena(), out, 2) diff --git a/Tests/test_imagemode.py b/Tests/test_imagemode.py deleted file mode 100644 index 54b04435f..000000000 --- a/Tests/test_imagemode.py +++ /dev/null @@ -1,23 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageMode - -ImageMode.getmode("1") -ImageMode.getmode("L") -ImageMode.getmode("P") -ImageMode.getmode("RGB") -ImageMode.getmode("I") -ImageMode.getmode("F") - -m = ImageMode.getmode("1") -assert_equal(m.mode, "1") -assert_equal(m.bands, ("1",)) -assert_equal(m.basemode, "L") -assert_equal(m.basetype, "L") - -m = ImageMode.getmode("RGB") -assert_equal(m.mode, "RGB") -assert_equal(m.bands, ("R", "G", "B")) -assert_equal(m.basemode, "RGB") -assert_equal(m.basetype, "L") diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py deleted file mode 100644 index 99ec005c8..000000000 --- a/Tests/test_imageshow.py +++ /dev/null @@ -1,6 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageShow - -success() diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py deleted file mode 100644 index b30971e8f..000000000 --- a/Tests/test_imagetk.py +++ /dev/null @@ -1,9 +0,0 @@ -from tester import * - -from PIL import Image -try: - from PIL import ImageTk -except (OSError, ImportError) as v: - skip(v) - -success() diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py deleted file mode 100644 index 268a75b6f..000000000 --- a/Tests/test_imagewin.py +++ /dev/null @@ -1,6 +0,0 @@ -from tester import * - -from PIL import Image -from PIL import ImageWin - -success() diff --git a/test/helper.py b/test/helper.py index ebe651120..54739cf7b 100644 --- a/test/helper.py +++ b/test/helper.py @@ -60,18 +60,19 @@ class PillowTestCase(unittest.TestCase): def assert_warning(self, warn_class, func): import warnings + result = None with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter("always") # Hopefully trigger a warning. - func() + result = func() # Verify some things. self.assertEqual(len(w), 1) assert issubclass(w[-1].category, warn_class) self.assertIn("deprecated", str(w[-1].message)) - + return result # # require that deprecation warnings are triggered # import warnings diff --git a/test/test_file_ico.py b/test/test_file_ico.py new file mode 100644 index 000000000..dc289e1d2 --- /dev/null +++ b/test/test_file_ico.py @@ -0,0 +1,23 @@ +from helper import unittest, PillowTestCase + +from PIL import Image + +# sample ppm stream +file = "Images/lena.ico" +data = open(file, "rb").read() + + +class TestFileIco(PillowTestCase): + + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (16, 16)) + self.assertEqual(im.format, "ICO") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_font_bdf.py b/test/test_font_bdf.py new file mode 100644 index 000000000..b141e6149 --- /dev/null +++ b/test/test_font_bdf.py @@ -0,0 +1,22 @@ +from helper import unittest, PillowTestCase + +from PIL import FontFile, BdfFontFile + +filename = "Images/courB08.bdf" + + +class TestImage(PillowTestCase): + + def test_sanity(self): + + file = open(filename, "rb") + font = BdfFontFile.BdfFontFile(file) + + self.assertIsInstance(font, FontFile.FontFile) + self.assertEqual(len([_f for _f in font.glyph if _f]), 190) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_offset.py b/test/test_image_offset.py new file mode 100644 index 000000000..bb9b5a38c --- /dev/null +++ b/test/test_image_offset.py @@ -0,0 +1,25 @@ +from helper import unittest, PillowTestCase, lena + + +class TestImage(PillowTestCase): + + def test_offset(self): + + im1 = lena() + + im2 = self.assert_warning(DeprecationWarning, lambda: im1.offset(10)) + self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((10, 10))) + + im2 = self.assert_warning( + DeprecationWarning, lambda: im1.offset(10, 20)) + self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((10, 20))) + + im2 = self.assert_warning( + DeprecationWarning, lambda: im1.offset(20, 20)) + self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((20, 20))) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagecms.py b/test/test_imagecms.py new file mode 100644 index 000000000..1a31636e8 --- /dev/null +++ b/test/test_imagecms.py @@ -0,0 +1,214 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + +try: + from PIL import ImageCms + ImageCms.core.profile_open +except ImportError as v: + # Skipped via setUp() + pass + + +SRGB = "Tests/icc/sRGB.icm" + + +class TestImage(PillowTestCase): + + def setUp(self): + try: + from PIL import ImageCms + except ImportError as v: + self.skipTest(v) + + def test_sanity(self): + + # basic smoke test. + # this mostly follows the cms_test outline. + + v = ImageCms.versions() # should return four strings + self.assertEqual(v[0], '1.0.0 pil') + self.assertEqual(list(map(type, v)), [str, str, str, str]) + + # internal version number + self.assertRegexpMatches(ImageCms.core.littlecms_version, "\d+\.\d+$") + + i = ImageCms.profileToProfile(lena(), SRGB, SRGB) + self.assert_image(i, "RGB", (128, 128)) + + i = lena() + ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) + self.assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + self.assert_image(i, "RGB", (128, 128)) + + i = lena() + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + ImageCms.applyTransform(lena(), t, inPlace=True) + self.assert_image(i, "RGB", (128, 128)) + + p = ImageCms.createProfile("sRGB") + o = ImageCms.getOpenProfile(SRGB) + t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + self.assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") + self.assertEqual(t.inputMode, "RGB") + self.assertEqual(t.outputMode, "RGB") + i = ImageCms.applyTransform(lena(), t) + self.assert_image(i, "RGB", (128, 128)) + + # test PointTransform convenience API + lena().point(t) + + def test_name(self): + # get profile information for file + self.assertEqual( + ImageCms.getProfileName(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + def test_info(self): + self.assertEqual( + ImageCms.getProfileInfo(SRGB).splitlines(), [ + 'sRGB IEC61966-2.1', '', + 'Copyright (c) 1998 Hewlett-Packard Company', '']) + + def test_copyright(self): + self.assertEqual( + ImageCms.getProfileCopyright(SRGB).strip(), + 'Copyright (c) 1998 Hewlett-Packard Company') + + def test_manufacturer(self): + self.assertEqual( + ImageCms.getProfileManufacturer(SRGB).strip(), + 'IEC http://www.iec.ch') + + def test_model(self): + self.assertEqual( + ImageCms.getProfileModel(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + def test_description(self): + self.assertEqual( + ImageCms.getProfileDescription(SRGB).strip(), + 'sRGB IEC61966-2.1') + + def test_intent(self): + self.assertEqual(ImageCms.getDefaultIntent(SRGB), 0) + self.assertEqual(ImageCms.isIntentSupported( + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + def test_profile_object(self): + # same, using profile object + p = ImageCms.createProfile("sRGB") + # self.assertEqual(ImageCms.getProfileName(p).strip(), + # 'sRGB built-in - (lcms internal)') + # self.assertEqual(ImageCms.getProfileInfo(p).splitlines(), + # ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) + self.assertEqual(ImageCms.getDefaultIntent(p), 0) + self.assertEqual(ImageCms.isIntentSupported( + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + def test_extensions(self): + # extensions + from io import BytesIO + i = Image.open("Tests/images/rgb.jpg") + p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) + self.assertEqual( + ImageCms.getProfileName(p).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + def test_exceptions(self): + # the procedural pyCMS API uses PyCMSError for all sorts of errors + self.assertRaises( + ImageCms.PyCMSError, + lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) + self.assertRaises( + ImageCms.PyCMSError, + lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) + self.assertRaises( + ImageCms.PyCMSError, + lambda: ImageCms.getProfileName(None)) + self.assertRaises( + ImageCms.PyCMSError, + lambda: ImageCms.isIntentSupported(SRGB, None, None)) + + def test_display_profile(self): + # try fetching the profile for the current display device + ImageCms.get_display_profile() + + def test_lab_color_profile(self): + ImageCms.createProfile("LAB", 5000) + ImageCms.createProfile("LAB", 6500) + + def test_simple_lab(self): + i = Image.new('RGB', (10, 10), (128, 128, 128)) + + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + i_lab = ImageCms.applyTransform(i, t) + + self.assertEqual(i_lab.mode, 'LAB') + + k = i_lab.getpixel((0, 0)) + # not a linear luminance map. so L != 128: + self.assertEqual(k, (137, 128, 128)) + + L = i_lab.getdata(0) + a = i_lab.getdata(1) + b = i_lab.getdata(2) + + self.assertEqual(list(L), [137] * 100) + self.assertEqual(list(a), [128] * 100) + self.assertEqual(list(b), [128] * 100) + + def test_lab_color(self): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + # Need to add a type mapping for some PIL type to TYPE_Lab_8 in + # findLCMSType, and have that mapping work back to a PIL mode + # (likely RGB). + i = ImageCms.applyTransform(lena(), t) + self.assert_image(i, "LAB", (128, 128)) + + # i.save('temp.lab.tif') # visually verified vs PS. + + target = Image.open('Tests/images/lena.Lab.tif') + + self.assert_image_similar(i, target, 30) + + def test_lab_srgb(self): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + img = Image.open('Tests/images/lena.Lab.tif') + + img_srgb = ImageCms.applyTransform(img, t) + + # img_srgb.save('temp.srgb.tif') # visually verified vs ps. + + self.assert_image_similar(lena(), img_srgb, 30) + + def test_lab_roundtrip(self): + # check to see if we're at least internally consistent. + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + i = ImageCms.applyTransform(lena(), t) + out = ImageCms.applyTransform(i, t2) + + self.assert_image_similar(lena(), out, 2) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagemode.py b/test/test_imagemode.py new file mode 100644 index 000000000..7fb596b46 --- /dev/null +++ b/test/test_imagemode.py @@ -0,0 +1,32 @@ +from helper import unittest, PillowTestCase + +from PIL import ImageMode + + +class TestImage(PillowTestCase): + + def test_sanity(self): + ImageMode.getmode("1") + ImageMode.getmode("L") + ImageMode.getmode("P") + ImageMode.getmode("RGB") + ImageMode.getmode("I") + ImageMode.getmode("F") + + m = ImageMode.getmode("1") + self.assertEqual(m.mode, "1") + self.assertEqual(m.bands, ("1",)) + self.assertEqual(m.basemode, "L") + self.assertEqual(m.basetype, "L") + + m = ImageMode.getmode("RGB") + self.assertEqual(m.mode, "RGB") + self.assertEqual(m.bands, ("R", "G", "B")) + self.assertEqual(m.basemode, "RGB") + self.assertEqual(m.basetype, "L") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imageshow.py b/test/test_imageshow.py new file mode 100644 index 000000000..12594c98f --- /dev/null +++ b/test/test_imageshow.py @@ -0,0 +1,18 @@ +from helper import unittest, PillowTestCase + +from PIL import Image +from PIL import ImageShow + + +class TestImage(PillowTestCase): + + def test_sanity(self): + dir(Image) + dir(ImageShow) + pass + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagetk.py b/test/test_imagetk.py new file mode 100644 index 000000000..87a07e288 --- /dev/null +++ b/test/test_imagetk.py @@ -0,0 +1,17 @@ +from helper import unittest, PillowTestCase + + +class TestImageTk(PillowTestCase): + + def test_import(self): + try: + from PIL import ImageTk + dir(ImageTk) + except (OSError, ImportError) as v: + self.skipTest(v) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagewin.py b/test/test_imagewin.py new file mode 100644 index 000000000..f7a6bc0dc --- /dev/null +++ b/test/test_imagewin.py @@ -0,0 +1,18 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image +from PIL import ImageWin + + +class TestImage(PillowTestCase): + + def test_sanity(self): + dir(Image) + dir(ImageShow) + pass + + +if __name__ == '__main__': + unittest.main() + +# End of file From 3c7c89e642518de369c7c6a30f21faebbddf2824 Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 17:05:54 +0300 Subject: [PATCH 129/168] Convert more tests, fix test_imagewin.py --- Tests/test_image_getpalette.py | 19 ----------------- Tests/test_image_mode.py | 27 ------------------------ Tests/test_image_quantize.py | 27 ------------------------ {Tests => test}/test_file_xbm.py | 22 +++++++++++++------ test/test_image_getpalette.py | 26 +++++++++++++++++++++++ test/test_image_mode.py | 36 ++++++++++++++++++++++++++++++++ test/test_image_quantize.py | 35 +++++++++++++++++++++++++++++++ test/test_imagewin.py | 2 +- 8 files changed, 114 insertions(+), 80 deletions(-) delete mode 100644 Tests/test_image_getpalette.py delete mode 100644 Tests/test_image_mode.py delete mode 100644 Tests/test_image_quantize.py rename {Tests => test}/test_file_xbm.py (72%) create mode 100644 test/test_image_getpalette.py create mode 100644 test/test_image_mode.py create mode 100644 test/test_image_quantize.py diff --git a/Tests/test_image_getpalette.py b/Tests/test_image_getpalette.py deleted file mode 100644 index 5dc923b9f..000000000 --- a/Tests/test_image_getpalette.py +++ /dev/null @@ -1,19 +0,0 @@ -from tester import * - -from PIL import Image - -def test_palette(): - def palette(mode): - p = lena(mode).getpalette() - if p: - return p[:10] - return None - assert_equal(palette("1"), None) - assert_equal(palette("L"), None) - assert_equal(palette("I"), None) - assert_equal(palette("F"), None) - assert_equal(palette("P"), [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - assert_equal(palette("RGB"), None) - assert_equal(palette("RGBA"), None) - assert_equal(palette("CMYK"), None) - assert_equal(palette("YCbCr"), None) diff --git a/Tests/test_image_mode.py b/Tests/test_image_mode.py deleted file mode 100644 index cd5bd47f5..000000000 --- a/Tests/test_image_mode.py +++ /dev/null @@ -1,27 +0,0 @@ -from tester import * - -from PIL import Image - -def test_sanity(): - - im = lena() - assert_no_exception(lambda: im.mode) - -def test_properties(): - def check(mode, *result): - signature = ( - Image.getmodebase(mode), Image.getmodetype(mode), - Image.getmodebands(mode), Image.getmodebandnames(mode), - ) - assert_equal(signature, result) - check("1", "L", "L", 1, ("1",)) - check("L", "L", "L", 1, ("L",)) - check("P", "RGB", "L", 1, ("P",)) - check("I", "L", "I", 1, ("I",)) - check("F", "L", "F", 1, ("F",)) - check("RGB", "RGB", "L", 3, ("R", "G", "B")) - check("RGBA", "RGB", "L", 4, ("R", "G", "B", "A")) - check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) - check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) - check("CMYK", "RGB", "L", 4, ("C", "M", "Y", "K")) - check("YCbCr", "RGB", "L", 3, ("Y", "Cb", "Cr")) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py deleted file mode 100644 index dbf68a25e..000000000 --- a/Tests/test_image_quantize.py +++ /dev/null @@ -1,27 +0,0 @@ -from tester import * - -from PIL import Image - -def test_sanity(): - - im = lena() - - im = im.quantize() - assert_image(im, "P", im.size) - - im = lena() - im = im.quantize(palette=lena("P")) - assert_image(im, "P", im.size) - -def test_octree_quantize(): - im = lena() - - im = im.quantize(100, Image.FASTOCTREE) - assert_image(im, "P", im.size) - - assert len(im.getcolors()) == 100 - -def test_rgba_quantize(): - im = lena('RGBA') - assert_no_exception(lambda: im.quantize()) - assert_exception(Exception, lambda: im.quantize(method=0)) diff --git a/Tests/test_file_xbm.py b/test/test_file_xbm.py similarity index 72% rename from Tests/test_file_xbm.py rename to test/test_file_xbm.py index f27a3a349..02aec70b1 100644 --- a/Tests/test_file_xbm.py +++ b/test/test_file_xbm.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase from PIL import Image @@ -25,10 +25,20 @@ static char basic_bits[] = { }; """ -def test_pil151(): - im = Image.open(BytesIO(PIL151)) +class TestFileXbm(PillowTestCase): - assert_no_exception(lambda: im.load()) - assert_equal(im.mode, '1') - assert_equal(im.size, (32, 32)) + def test_pil151(self): + from io import BytesIO + + im = Image.open(BytesIO(PIL151)) + + im.load() + self.assertEqual(im.mode, '1') + self.assertEqual(im.size, (32, 32)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_getpalette.py b/test/test_image_getpalette.py new file mode 100644 index 000000000..0c399c432 --- /dev/null +++ b/test/test_image_getpalette.py @@ -0,0 +1,26 @@ +from helper import unittest, PillowTestCase, lena + + +class TestImageGetPalette(PillowTestCase): + + def test_palette(self): + def palette(mode): + p = lena(mode).getpalette() + if p: + return p[:10] + return None + self.assertEqual(palette("1"), None) + self.assertEqual(palette("L"), None) + self.assertEqual(palette("I"), None) + self.assertEqual(palette("F"), None) + self.assertEqual(palette("P"), [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + self.assertEqual(palette("RGB"), None) + self.assertEqual(palette("RGBA"), None) + self.assertEqual(palette("CMYK"), None) + self.assertEqual(palette("YCbCr"), None) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_mode.py b/test/test_image_mode.py new file mode 100644 index 000000000..d229a27a2 --- /dev/null +++ b/test/test_image_mode.py @@ -0,0 +1,36 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + + +class TestImage(PillowTestCase): + + def test_sanity(self): + + im = lena() + im.mode + + def test_properties(self): + def check(mode, *result): + signature = ( + Image.getmodebase(mode), Image.getmodetype(mode), + Image.getmodebands(mode), Image.getmodebandnames(mode), + ) + self.assertEqual(signature, result) + check("1", "L", "L", 1, ("1",)) + check("L", "L", "L", 1, ("L",)) + check("P", "RGB", "L", 1, ("P",)) + check("I", "L", "I", 1, ("I",)) + check("F", "L", "F", 1, ("F",)) + check("RGB", "RGB", "L", 3, ("R", "G", "B")) + check("RGBA", "RGB", "L", 4, ("R", "G", "B", "A")) + check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) + check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) + check("CMYK", "RGB", "L", 4, ("C", "M", "Y", "K")) + check("YCbCr", "RGB", "L", 3, ("Y", "Cb", "Cr")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_image_quantize.py b/test/test_image_quantize.py new file mode 100644 index 000000000..35f876717 --- /dev/null +++ b/test/test_image_quantize.py @@ -0,0 +1,35 @@ +from helper import unittest, PillowTestCase, lena + +from PIL import Image + + +class TestImage(PillowTestCase): + + def test_sanity(self): + im = lena() + + im = im.quantize() + self.assert_image(im, "P", im.size) + + im = lena() + im = im.quantize(palette=lena("P")) + self.assert_image(im, "P", im.size) + + def test_octree_quantize(self): + im = lena() + + im = im.quantize(100, Image.FASTOCTREE) + self.assert_image(im, "P", im.size) + + assert len(im.getcolors()) == 100 + + def test_rgba_quantize(self): + im = lena('RGBA') + im.quantize() + self.assertRaises(Exception, lambda: im.quantize(method=0)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_imagewin.py b/test/test_imagewin.py index f7a6bc0dc..916abc77b 100644 --- a/test/test_imagewin.py +++ b/test/test_imagewin.py @@ -8,7 +8,7 @@ class TestImage(PillowTestCase): def test_sanity(self): dir(Image) - dir(ImageShow) + dir(ImageWin) pass From 5919c87afb1a6c6f1ab1d2787e0c79fee5e1ad5f Mon Sep 17 00:00:00 2001 From: hugovk Date: Fri, 6 Jun 2014 17:13:29 +0300 Subject: [PATCH 130/168] Convert more tests --- Tests/test_image_getextrema.py | 17 ---- Tests/test_lib_pack.py | 138 ------------------------------- test/test_image_getextrema.py | 27 ++++++ test/test_lib_pack.py | 147 +++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 155 deletions(-) delete mode 100644 Tests/test_image_getextrema.py delete mode 100644 Tests/test_lib_pack.py create mode 100644 test/test_image_getextrema.py create mode 100644 test/test_lib_pack.py diff --git a/Tests/test_image_getextrema.py b/Tests/test_image_getextrema.py deleted file mode 100644 index 86106cde0..000000000 --- a/Tests/test_image_getextrema.py +++ /dev/null @@ -1,17 +0,0 @@ -from tester import * - -from PIL import Image - -def test_extrema(): - - def extrema(mode): - return lena(mode).getextrema() - - assert_equal(extrema("1"), (0, 255)) - assert_equal(extrema("L"), (40, 235)) - assert_equal(extrema("I"), (40, 235)) - assert_equal(extrema("F"), (40.0, 235.0)) - assert_equal(extrema("P"), (11, 218)) # fixed palette - assert_equal(extrema("RGB"), ((61, 255), (26, 234), (44, 223))) - assert_equal(extrema("RGBA"), ((61, 255), (26, 234), (44, 223), (255, 255))) - assert_equal(extrema("CMYK"), ((0, 194), (21, 229), (32, 211), (0, 0))) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py deleted file mode 100644 index 7675348b3..000000000 --- a/Tests/test_lib_pack.py +++ /dev/null @@ -1,138 +0,0 @@ -from tester import * - -from PIL import Image - -def pack(): - pass # not yet - -def test_pack(): - - def pack(mode, rawmode): - if len(mode) == 1: - im = Image.new(mode, (1, 1), 1) - else: - im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) - - if py3: - return list(im.tobytes("raw", rawmode)) - else: - return [ord(c) for c in im.tobytes("raw", rawmode)] - - order = 1 if Image._ENDIAN == '<' else -1 - - assert_equal(pack("1", "1"), [128]) - assert_equal(pack("1", "1;I"), [0]) - assert_equal(pack("1", "1;R"), [1]) - assert_equal(pack("1", "1;IR"), [0]) - - assert_equal(pack("L", "L"), [1]) - - assert_equal(pack("I", "I"), [1, 0, 0, 0][::order]) - - assert_equal(pack("F", "F"), [0, 0, 128, 63][::order]) - - assert_equal(pack("LA", "LA"), [1, 2]) - - assert_equal(pack("RGB", "RGB"), [1, 2, 3]) - assert_equal(pack("RGB", "RGB;L"), [1, 2, 3]) - assert_equal(pack("RGB", "BGR"), [3, 2, 1]) - assert_equal(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? - assert_equal(pack("RGB", "BGRX"), [3, 2, 1, 0]) - assert_equal(pack("RGB", "XRGB"), [0, 1, 2, 3]) - assert_equal(pack("RGB", "XBGR"), [0, 3, 2, 1]) - - assert_equal(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? - - assert_equal(pack("RGBA", "RGBA"), [1, 2, 3, 4]) - - assert_equal(pack("CMYK", "CMYK"), [1, 2, 3, 4]) - assert_equal(pack("YCbCr", "YCbCr"), [1, 2, 3]) - -def test_unpack(): - - def unpack(mode, rawmode, bytes_): - im = None - - if py3: - data = bytes(range(1,bytes_+1)) - else: - data = ''.join(chr(i) for i in range(1,bytes_+1)) - - im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) - - return im.getpixel((0, 0)) - - def unpack_1(mode, rawmode, value): - assert mode == "1" - im = None - - if py3: - im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) - else: - im = Image.frombytes(mode, (8, 1), chr(value), "raw", rawmode, 0, 1) - - return tuple(im.getdata()) - - X = 255 - - assert_equal(unpack_1("1", "1", 1), (0,0,0,0,0,0,0,X)) - assert_equal(unpack_1("1", "1;I", 1), (X,X,X,X,X,X,X,0)) - assert_equal(unpack_1("1", "1;R", 1), (X,0,0,0,0,0,0,0)) - assert_equal(unpack_1("1", "1;IR", 1), (0,X,X,X,X,X,X,X)) - - assert_equal(unpack_1("1", "1", 170), (X,0,X,0,X,0,X,0)) - assert_equal(unpack_1("1", "1;I", 170), (0,X,0,X,0,X,0,X)) - assert_equal(unpack_1("1", "1;R", 170), (0,X,0,X,0,X,0,X)) - assert_equal(unpack_1("1", "1;IR", 170), (X,0,X,0,X,0,X,0)) - - assert_equal(unpack("L", "L;2", 1), 0) - assert_equal(unpack("L", "L;4", 1), 0) - assert_equal(unpack("L", "L", 1), 1) - assert_equal(unpack("L", "L;I", 1), 254) - assert_equal(unpack("L", "L;R", 1), 128) - assert_equal(unpack("L", "L;16", 2), 2) # little endian - assert_equal(unpack("L", "L;16B", 2), 1) # big endian - - assert_equal(unpack("LA", "LA", 2), (1, 2)) - assert_equal(unpack("LA", "LA;L", 2), (1, 2)) - - assert_equal(unpack("RGB", "RGB", 3), (1, 2, 3)) - assert_equal(unpack("RGB", "RGB;L", 3), (1, 2, 3)) - assert_equal(unpack("RGB", "RGB;R", 3), (128, 64, 192)) - assert_equal(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? - assert_equal(unpack("RGB", "BGR", 3), (3, 2, 1)) - assert_equal(unpack("RGB", "RGB;15", 2), (8, 131, 0)) - assert_equal(unpack("RGB", "BGR;15", 2), (0, 131, 8)) - assert_equal(unpack("RGB", "RGB;16", 2), (8, 64, 0)) - assert_equal(unpack("RGB", "BGR;16", 2), (0, 64, 8)) - assert_equal(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) - - assert_equal(unpack("RGB", "RGBX", 4), (1, 2, 3)) - assert_equal(unpack("RGB", "BGRX", 4), (3, 2, 1)) - assert_equal(unpack("RGB", "XRGB", 4), (2, 3, 4)) - assert_equal(unpack("RGB", "XBGR", 4), (4, 3, 2)) - - assert_equal(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) - assert_equal(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) - assert_equal(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) - assert_equal(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) - assert_equal(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) - assert_equal(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) - assert_equal(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) - - assert_equal(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? - assert_equal(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) - assert_equal(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) - assert_equal(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) - assert_equal(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) - assert_equal(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) - assert_equal(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) - - assert_equal(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) - assert_equal(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) - - assert_exception(ValueError, lambda: unpack("L", "L", 0)) - assert_exception(ValueError, lambda: unpack("RGB", "RGB", 2)) - assert_exception(ValueError, lambda: unpack("CMYK", "CMYK", 2)) - -run() diff --git a/test/test_image_getextrema.py b/test/test_image_getextrema.py new file mode 100644 index 000000000..af7f7698a --- /dev/null +++ b/test/test_image_getextrema.py @@ -0,0 +1,27 @@ +from helper import unittest, PillowTestCase, lena + + +class TestImageGetExtrema(PillowTestCase): + + def test_extrema(self): + + def extrema(mode): + return lena(mode).getextrema() + + self.assertEqual(extrema("1"), (0, 255)) + self.assertEqual(extrema("L"), (40, 235)) + self.assertEqual(extrema("I"), (40, 235)) + self.assertEqual(extrema("F"), (40.0, 235.0)) + self.assertEqual(extrema("P"), (11, 218)) # fixed palette + self.assertEqual( + extrema("RGB"), ((61, 255), (26, 234), (44, 223))) + self.assertEqual( + extrema("RGBA"), ((61, 255), (26, 234), (44, 223), (255, 255))) + self.assertEqual( + extrema("CMYK"), ((0, 194), (21, 229), (32, 211), (0, 0))) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/test/test_lib_pack.py b/test/test_lib_pack.py new file mode 100644 index 000000000..102835b58 --- /dev/null +++ b/test/test_lib_pack.py @@ -0,0 +1,147 @@ +from helper import unittest, PillowTestCase, py3 + +from PIL import Image + + +class TestLibPack(PillowTestCase): + + def pack(self): + pass # not yet + + def test_pack(self): + + def pack(mode, rawmode): + if len(mode) == 1: + im = Image.new(mode, (1, 1), 1) + else: + im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) + + if py3: + return list(im.tobytes("raw", rawmode)) + else: + return [ord(c) for c in im.tobytes("raw", rawmode)] + + order = 1 if Image._ENDIAN == '<' else -1 + + self.assertEqual(pack("1", "1"), [128]) + self.assertEqual(pack("1", "1;I"), [0]) + self.assertEqual(pack("1", "1;R"), [1]) + self.assertEqual(pack("1", "1;IR"), [0]) + + self.assertEqual(pack("L", "L"), [1]) + + self.assertEqual(pack("I", "I"), [1, 0, 0, 0][::order]) + + self.assertEqual(pack("F", "F"), [0, 0, 128, 63][::order]) + + self.assertEqual(pack("LA", "LA"), [1, 2]) + + self.assertEqual(pack("RGB", "RGB"), [1, 2, 3]) + self.assertEqual(pack("RGB", "RGB;L"), [1, 2, 3]) + self.assertEqual(pack("RGB", "BGR"), [3, 2, 1]) + self.assertEqual(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? + self.assertEqual(pack("RGB", "BGRX"), [3, 2, 1, 0]) + self.assertEqual(pack("RGB", "XRGB"), [0, 1, 2, 3]) + self.assertEqual(pack("RGB", "XBGR"), [0, 3, 2, 1]) + + self.assertEqual(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? + + self.assertEqual(pack("RGBA", "RGBA"), [1, 2, 3, 4]) + + self.assertEqual(pack("CMYK", "CMYK"), [1, 2, 3, 4]) + self.assertEqual(pack("YCbCr", "YCbCr"), [1, 2, 3]) + + def test_unpack(self): + + def unpack(mode, rawmode, bytes_): + im = None + + if py3: + data = bytes(range(1, bytes_+1)) + else: + data = ''.join(chr(i) for i in range(1, bytes_+1)) + + im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) + + return im.getpixel((0, 0)) + + def unpack_1(mode, rawmode, value): + assert mode == "1" + im = None + + if py3: + im = Image.frombytes( + mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) + else: + im = Image.frombytes( + mode, (8, 1), chr(value), "raw", rawmode, 0, 1) + + return tuple(im.getdata()) + + X = 255 + + self.assertEqual(unpack_1("1", "1", 1), (0, 0, 0, 0, 0, 0, 0, X)) + self.assertEqual(unpack_1("1", "1;I", 1), (X, X, X, X, X, X, X, 0)) + self.assertEqual(unpack_1("1", "1;R", 1), (X, 0, 0, 0, 0, 0, 0, 0)) + self.assertEqual(unpack_1("1", "1;IR", 1), (0, X, X, X, X, X, X, X)) + + self.assertEqual(unpack_1("1", "1", 170), (X, 0, X, 0, X, 0, X, 0)) + self.assertEqual(unpack_1("1", "1;I", 170), (0, X, 0, X, 0, X, 0, X)) + self.assertEqual(unpack_1("1", "1;R", 170), (0, X, 0, X, 0, X, 0, X)) + self.assertEqual(unpack_1("1", "1;IR", 170), (X, 0, X, 0, X, 0, X, 0)) + + self.assertEqual(unpack("L", "L;2", 1), 0) + self.assertEqual(unpack("L", "L;4", 1), 0) + self.assertEqual(unpack("L", "L", 1), 1) + self.assertEqual(unpack("L", "L;I", 1), 254) + self.assertEqual(unpack("L", "L;R", 1), 128) + self.assertEqual(unpack("L", "L;16", 2), 2) # little endian + self.assertEqual(unpack("L", "L;16B", 2), 1) # big endian + + self.assertEqual(unpack("LA", "LA", 2), (1, 2)) + self.assertEqual(unpack("LA", "LA;L", 2), (1, 2)) + + self.assertEqual(unpack("RGB", "RGB", 3), (1, 2, 3)) + self.assertEqual(unpack("RGB", "RGB;L", 3), (1, 2, 3)) + self.assertEqual(unpack("RGB", "RGB;R", 3), (128, 64, 192)) + self.assertEqual(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? + self.assertEqual(unpack("RGB", "BGR", 3), (3, 2, 1)) + self.assertEqual(unpack("RGB", "RGB;15", 2), (8, 131, 0)) + self.assertEqual(unpack("RGB", "BGR;15", 2), (0, 131, 8)) + self.assertEqual(unpack("RGB", "RGB;16", 2), (8, 64, 0)) + self.assertEqual(unpack("RGB", "BGR;16", 2), (0, 64, 8)) + self.assertEqual(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) + + self.assertEqual(unpack("RGB", "RGBX", 4), (1, 2, 3)) + self.assertEqual(unpack("RGB", "BGRX", 4), (3, 2, 1)) + self.assertEqual(unpack("RGB", "XRGB", 4), (2, 3, 4)) + self.assertEqual(unpack("RGB", "XBGR", 4), (4, 3, 2)) + + self.assertEqual(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) + self.assertEqual(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) + self.assertEqual(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) + self.assertEqual(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) + self.assertEqual(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) + self.assertEqual(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) + self.assertEqual(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) + + self.assertEqual(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? + self.assertEqual(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) + self.assertEqual(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) + self.assertEqual(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) + self.assertEqual(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) + self.assertEqual(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) + self.assertEqual(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) + + self.assertEqual(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) + self.assertEqual(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) + + self.assertRaises(ValueError, lambda: unpack("L", "L", 0)) + self.assertRaises(ValueError, lambda: unpack("RGB", "RGB", 2)) + self.assertRaises(ValueError, lambda: unpack("CMYK", "CMYK", 2)) + + +if __name__ == '__main__': + unittest.main() + +# End of file From 9415407b833d025a7153443d39b6a688deaaa82f Mon Sep 17 00:00:00 2001 From: Ben Williams Date: Fri, 6 Jun 2014 21:42:20 +0100 Subject: [PATCH 131/168] Fix a its/it's incorrect usage --- PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index e064ed9ef..fe4f47295 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -522,7 +522,7 @@ class Image: """ Closes the file pointer, if possible. - This operation will destroy the image core and release it's memory. + This operation will destroy the image core and release its memory. The image data will be unusable afterward. This function is only required to close images that have not From 8693c63173928fb18bdd82fb7f3a450ddab95bfd Mon Sep 17 00:00:00 2001 From: hugovk Date: Sat, 7 Jun 2014 00:23:55 +0300 Subject: [PATCH 132/168] assert_warning() checks in case of multiple warnings --- test/helper.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/helper.py b/test/helper.py index 54739cf7b..567fc3945 100644 --- a/test/helper.py +++ b/test/helper.py @@ -69,9 +69,13 @@ class PillowTestCase(unittest.TestCase): result = func() # Verify some things. - self.assertEqual(len(w), 1) - assert issubclass(w[-1].category, warn_class) - self.assertIn("deprecated", str(w[-1].message)) + self.assertGreaterEqual(len(w), 1) + found = False + for v in w: + if issubclass(v.category, warn_class): + found = True + break + self.assertTrue(found) return result # # require that deprecation warnings are triggered From 1d96522932927f18eb2b7f3b35608ab94a9a86ce Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 8 Jun 2014 07:02:58 -0400 Subject: [PATCH 133/168] Update --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 71a42e315..40d8ed5cd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Use unittest for tests + [hugovk] + - ImageCms fixes [hugovk] From b7f94e3609b027f108b75f89bf63f07e54262d7d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 9 Jun 2014 20:03:57 -0400 Subject: [PATCH 134/168] Add test runner --- setup.py | 2 +- test/__init__.py | 0 test/helper.py | 3 +++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 test/__init__.py diff --git a/setup.py b/setup.py index 83defd82b..45bddf937 100644 --- a/setup.py +++ b/setup.py @@ -680,7 +680,7 @@ setup( include_package_data=True, packages=find_packages(), scripts=glob.glob("Scripts/pil*.py"), - test_suite='PIL.tests', + test_suite='test.helper.TestSuite', keywords=["Imaging",], license='Standard PIL License', zip_safe=True, diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/helper.py b/test/helper.py index 567fc3945..588b4f0ec 100644 --- a/test/helper.py +++ b/test/helper.py @@ -308,3 +308,6 @@ def lena(mode="RGB", cache={}): # # # _setup() + +TestLoader = unittest.TestLoader() +TestSuite = TestLoader.discover(start_dir='.') From 7a10f6b39a2c1f191c977571e69ceafe8022c006 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 9 Jun 2014 20:21:31 -0400 Subject: [PATCH 135/168] Experimental import fix Why would this work local but not on travis? --- test/test_000_sanity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_000_sanity.py b/test/test_000_sanity.py index 22e582ec3..fda481ed1 100644 --- a/test/test_000_sanity.py +++ b/test/test_000_sanity.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase import PIL import PIL.Image From 0c47ce00b1d9e4801a2043e0e14ba62e896da2a9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 9 Jun 2014 20:36:40 -0400 Subject: [PATCH 136/168] Fix broken py3 tests --- test/test_bmp_reference.py | 2 +- test/test_file_fli.py | 2 +- test/test_file_icns.py | 2 +- test/test_file_ico.py | 2 +- test/test_file_psd.py | 2 +- test/test_file_xbm.py | 2 +- test/test_file_xpm.py | 2 +- test/test_font_bdf.py | 2 +- test/test_format_lab.py | 2 +- test/test_image_array.py | 2 +- test/test_image_copy.py | 2 +- test/test_image_crop.py | 2 +- test/test_image_filter.py | 2 +- test/test_image_frombytes.py | 2 +- test/test_image_getbands.py | 2 +- test/test_image_getbbox.py | 2 +- test/test_image_getcolors.py | 2 +- test/test_image_getextrema.py | 2 +- test/test_image_getim.py | 2 +- test/test_image_getpalette.py | 2 +- test/test_image_histogram.py | 2 +- test/test_image_load.py | 2 +- test/test_image_mode.py | 2 +- test/test_image_offset.py | 2 +- test/test_image_putalpha.py | 2 +- test/test_image_putdata.py | 2 +- test/test_image_putpalette.py | 2 +- test/test_image_quantize.py | 2 +- test/test_image_resize.py | 2 +- test/test_image_rotate.py | 2 +- test/test_image_thumbnail.py | 2 +- test/test_image_tobitmap.py | 2 +- test/test_image_tobytes.py | 2 +- test/test_image_transform.py | 2 +- test/test_imagechops.py | 2 +- test/test_imagecms.py | 2 +- test/test_imagecolor.py | 2 +- test/test_imagedraw.py | 2 +- test/test_imageenhance.py | 2 +- test/test_imagefileio.py | 2 +- test/test_imagefilter.py | 2 +- test/test_imagefont.py | 2 +- test/test_imagegrab.py | 2 +- test/test_imagemath.py | 2 +- test/test_imagemode.py | 2 +- test/test_imageops.py | 2 +- test/test_imageshow.py | 2 +- test/test_imagestat.py | 2 +- test/test_imagetk.py | 2 +- test/test_imagetransform.py | 2 +- test/test_imagewin.py | 2 +- test/test_lib_image.py | 2 +- test/test_lib_pack.py | 2 +- test/test_locale.py | 2 +- 54 files changed, 54 insertions(+), 54 deletions(-) diff --git a/test/test_bmp_reference.py b/test/test_bmp_reference.py index ed012e7c8..ce621b38a 100644 --- a/test/test_bmp_reference.py +++ b/test/test_bmp_reference.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image import os diff --git a/test/test_file_fli.py b/test/test_file_fli.py index dd22a58f9..3479a4271 100644 --- a/test/test_file_fli.py +++ b/test/test_file_fli.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_icns.py b/test/test_file_icns.py index 9d838620b..d65af2ecf 100644 --- a/test/test_file_icns.py +++ b/test/test_file_icns.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_ico.py b/test/test_file_ico.py index dc289e1d2..9e524561c 100644 --- a/test/test_file_ico.py +++ b/test/test_file_ico.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_psd.py b/test/test_file_psd.py index de3d6f33d..b30b95bff 100644 --- a/test/test_file_psd.py +++ b/test/test_file_psd.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_xbm.py b/test/test_file_xbm.py index 02aec70b1..0a4f50a49 100644 --- a/test/test_file_xbm.py +++ b/test/test_file_xbm.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_xpm.py b/test/test_file_xpm.py index ecbb4137a..c74232812 100644 --- a/test/test_file_xpm.py +++ b/test/test_file_xpm.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_font_bdf.py b/test/test_font_bdf.py index b141e6149..9e6a38dd9 100644 --- a/test/test_font_bdf.py +++ b/test/test_font_bdf.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import FontFile, BdfFontFile diff --git a/test/test_format_lab.py b/test/test_format_lab.py index 53468db5f..36e84801e 100644 --- a/test/test_format_lab.py +++ b/test/test_format_lab.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_image_array.py b/test/test_image_array.py index a0f5f29e1..3e30019fe 100644 --- a/test/test_image_array.py +++ b/test/test_image_array.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_copy.py b/test/test_image_copy.py index a7882db94..7c668a464 100644 --- a/test/test_image_copy.py +++ b/test/test_image_copy.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_crop.py b/test/test_image_crop.py index da93fe7c8..bcb097e25 100644 --- a/test/test_image_crop.py +++ b/test/test_image_crop.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_filter.py b/test/test_image_filter.py index 4a85b0a2e..373d48356 100644 --- a/test/test_image_filter.py +++ b/test/test_image_filter.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageFilter diff --git a/test/test_image_frombytes.py b/test/test_image_frombytes.py index aad8046a1..92885f9a9 100644 --- a/test/test_image_frombytes.py +++ b/test/test_image_frombytes.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_getbands.py b/test/test_image_getbands.py index e803abb02..03ff3914b 100644 --- a/test/test_image_getbands.py +++ b/test/test_image_getbands.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_image_getbbox.py b/test/test_image_getbbox.py index 83a6a3dec..1f01bcee3 100644 --- a/test/test_image_getbbox.py +++ b/test/test_image_getbbox.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_getcolors.py b/test/test_image_getcolors.py index cfc827b28..13c10ad86 100644 --- a/test/test_image_getcolors.py +++ b/test/test_image_getcolors.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena class TestImage(PillowTestCase): diff --git a/test/test_image_getextrema.py b/test/test_image_getextrema.py index af7f7698a..77521294e 100644 --- a/test/test_image_getextrema.py +++ b/test/test_image_getextrema.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena class TestImageGetExtrema(PillowTestCase): diff --git a/test/test_image_getim.py b/test/test_image_getim.py index d498d3923..47283d97b 100644 --- a/test/test_image_getim.py +++ b/test/test_image_getim.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena, py3 +from test.helper import unittest, PillowTestCase, lena, py3 class TestImageGetIm(PillowTestCase): diff --git a/test/test_image_getpalette.py b/test/test_image_getpalette.py index 0c399c432..d9184412a 100644 --- a/test/test_image_getpalette.py +++ b/test/test_image_getpalette.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena class TestImageGetPalette(PillowTestCase): diff --git a/test/test_image_histogram.py b/test/test_image_histogram.py index 70f78a1fb..4a0981009 100644 --- a/test/test_image_histogram.py +++ b/test/test_image_histogram.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena class TestImageHistogram(PillowTestCase): diff --git a/test/test_image_load.py b/test/test_image_load.py index 2001c233a..9c6f09388 100644 --- a/test/test_image_load.py +++ b/test/test_image_load.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_mode.py b/test/test_image_mode.py index d229a27a2..2c1bac609 100644 --- a/test/test_image_mode.py +++ b/test/test_image_mode.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_offset.py b/test/test_image_offset.py index bb9b5a38c..146e0af9c 100644 --- a/test/test_image_offset.py +++ b/test/test_image_offset.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena class TestImage(PillowTestCase): diff --git a/test/test_image_putalpha.py b/test/test_image_putalpha.py index 85c7ac262..0ea1215f9 100644 --- a/test/test_image_putalpha.py +++ b/test/test_image_putalpha.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_image_putdata.py b/test/test_image_putdata.py index c7c3115aa..be8e6bc22 100644 --- a/test/test_image_putdata.py +++ b/test/test_image_putdata.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena import sys diff --git a/test/test_image_putpalette.py b/test/test_image_putpalette.py index b77dcbf00..d6daaa42b 100644 --- a/test/test_image_putpalette.py +++ b/test/test_image_putpalette.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import ImagePalette diff --git a/test/test_image_quantize.py b/test/test_image_quantize.py index 35f876717..c1bbdd134 100644 --- a/test/test_image_quantize.py +++ b/test/test_image_quantize.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_resize.py b/test/test_image_resize.py index 6c9932e45..62cd971c5 100644 --- a/test/test_image_resize.py +++ b/test/test_image_resize.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena class TestImageResize(PillowTestCase): diff --git a/test/test_image_rotate.py b/test/test_image_rotate.py index 531fdd63f..e00b5a4c5 100644 --- a/test/test_image_rotate.py +++ b/test/test_image_rotate.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena class TestImageRotate(PillowTestCase): diff --git a/test/test_image_thumbnail.py b/test/test_image_thumbnail.py index ee49be43e..1ab06d360 100644 --- a/test/test_image_thumbnail.py +++ b/test/test_image_thumbnail.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena class TestImageThumbnail(PillowTestCase): diff --git a/test/test_image_tobitmap.py b/test/test_image_tobitmap.py index 4e2a16df0..e7a66e712 100644 --- a/test/test_image_tobitmap.py +++ b/test/test_image_tobitmap.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena, fromstring +from test.helper import unittest, PillowTestCase, lena, fromstring class TestImage(PillowTestCase): diff --git a/test/test_image_tobytes.py b/test/test_image_tobytes.py index 3be9128c1..e1bfa29fd 100644 --- a/test/test_image_tobytes.py +++ b/test/test_image_tobytes.py @@ -1,4 +1,4 @@ -from helper import unittest, lena +from test.helper import unittest, lena class TestImageToBytes(unittest.TestCase): diff --git a/test/test_image_transform.py b/test/test_image_transform.py index 6ab186c12..1cf84c521 100644 --- a/test/test_image_transform.py +++ b/test/test_image_transform.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_imagechops.py b/test/test_imagechops.py index ec162d52f..5ffc02feb 100644 --- a/test/test_imagechops.py +++ b/test/test_imagechops.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageChops diff --git a/test/test_imagecms.py b/test/test_imagecms.py index 1a31636e8..7e59184dd 100644 --- a/test/test_imagecms.py +++ b/test/test_imagecms.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_imagecolor.py b/test/test_imagecolor.py index 5d8944852..2c41d0765 100644 --- a/test/test_imagecolor.py +++ b/test/test_imagecolor.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, lena from PIL import Image from PIL import ImageColor diff --git a/test/test_imagedraw.py b/test/test_imagedraw.py index 98876296f..9d5d575c5 100644 --- a/test/test_imagedraw.py +++ b/test/test_imagedraw.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageColor diff --git a/test/test_imageenhance.py b/test/test_imageenhance.py index 433c49cf6..d05f94655 100644 --- a/test/test_imageenhance.py +++ b/test/test_imageenhance.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageEnhance diff --git a/test/test_imagefileio.py b/test/test_imagefileio.py index 32ee0bc5e..4f96181ca 100644 --- a/test/test_imagefileio.py +++ b/test/test_imagefileio.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena, tostring +from test.helper import unittest, PillowTestCase, lena, tostring from PIL import Image from PIL import ImageFileIO diff --git a/test/test_imagefilter.py b/test/test_imagefilter.py index f7edb409a..19bd1a874 100644 --- a/test/test_imagefilter.py +++ b/test/test_imagefilter.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import ImageFilter diff --git a/test/test_imagefont.py b/test/test_imagefont.py index 17cb38cc2..2024ef2eb 100644 --- a/test/test_imagefont.py +++ b/test/test_imagefont.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image from PIL import ImageDraw diff --git a/test/test_imagegrab.py b/test/test_imagegrab.py index 2275d34a1..a3c7db9fc 100644 --- a/test/test_imagegrab.py +++ b/test/test_imagegrab.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase try: from PIL import ImageGrab diff --git a/test/test_imagemath.py b/test/test_imagemath.py index 17d43d25a..06b6dea08 100644 --- a/test/test_imagemath.py +++ b/test/test_imagemath.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image from PIL import ImageMath diff --git a/test/test_imagemode.py b/test/test_imagemode.py index 7fb596b46..c8480ec3f 100644 --- a/test/test_imagemode.py +++ b/test/test_imagemode.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import ImageMode diff --git a/test/test_imageops.py b/test/test_imageops.py index a4a94ca4d..e904b06ce 100644 --- a/test/test_imageops.py +++ b/test/test_imageops.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import ImageOps diff --git a/test/test_imageshow.py b/test/test_imageshow.py index 12594c98f..27af375e7 100644 --- a/test/test_imageshow.py +++ b/test/test_imageshow.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image from PIL import ImageShow diff --git a/test/test_imagestat.py b/test/test_imagestat.py index 4d30ff023..b15ee8000 100644 --- a/test/test_imagestat.py +++ b/test/test_imagestat.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase from PIL import Image from PIL import ImageStat diff --git a/test/test_imagetk.py b/test/test_imagetk.py index 87a07e288..aa10df0f4 100644 --- a/test/test_imagetk.py +++ b/test/test_imagetk.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase class TestImageTk(PillowTestCase): diff --git a/test/test_imagetransform.py b/test/test_imagetransform.py index f5741df32..5d7fc657f 100644 --- a/test/test_imagetransform.py +++ b/test/test_imagetransform.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image from PIL import ImageTransform diff --git a/test/test_imagewin.py b/test/test_imagewin.py index 916abc77b..a3ca7ee98 100644 --- a/test/test_imagewin.py +++ b/test/test_imagewin.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageWin diff --git a/test/test_lib_image.py b/test/test_lib_image.py index e0a903b00..99d1aa9b0 100644 --- a/test/test_lib_image.py +++ b/test/test_lib_image.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from test.helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_lib_pack.py b/test/test_lib_pack.py index 102835b58..d0162dbcb 100644 --- a/test/test_lib_pack.py +++ b/test/test_lib_pack.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, py3 +from test.helper import unittest, PillowTestCase, py3 from PIL import Image diff --git a/test/test_locale.py b/test/test_locale.py index 599e46266..77774dc31 100644 --- a/test/test_locale.py +++ b/test/test_locale.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, lena +from test.helper import unittest, PillowTestCase, lena from PIL import Image From 7a866e743137b67336b090ea641125f0d94cb91c Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 9 Jun 2014 20:56:29 -0400 Subject: [PATCH 137/168] Revert "Fix broken py3 tests" This reverts commit 0c47ce00b1d9e4801a2043e0e14ba62e896da2a9. --- test/test_bmp_reference.py | 2 +- test/test_file_fli.py | 2 +- test/test_file_icns.py | 2 +- test/test_file_ico.py | 2 +- test/test_file_psd.py | 2 +- test/test_file_xbm.py | 2 +- test/test_file_xpm.py | 2 +- test/test_font_bdf.py | 2 +- test/test_format_lab.py | 2 +- test/test_image_array.py | 2 +- test/test_image_copy.py | 2 +- test/test_image_crop.py | 2 +- test/test_image_filter.py | 2 +- test/test_image_frombytes.py | 2 +- test/test_image_getbands.py | 2 +- test/test_image_getbbox.py | 2 +- test/test_image_getcolors.py | 2 +- test/test_image_getextrema.py | 2 +- test/test_image_getim.py | 2 +- test/test_image_getpalette.py | 2 +- test/test_image_histogram.py | 2 +- test/test_image_load.py | 2 +- test/test_image_mode.py | 2 +- test/test_image_offset.py | 2 +- test/test_image_putalpha.py | 2 +- test/test_image_putdata.py | 2 +- test/test_image_putpalette.py | 2 +- test/test_image_quantize.py | 2 +- test/test_image_resize.py | 2 +- test/test_image_rotate.py | 2 +- test/test_image_thumbnail.py | 2 +- test/test_image_tobitmap.py | 2 +- test/test_image_tobytes.py | 2 +- test/test_image_transform.py | 2 +- test/test_imagechops.py | 2 +- test/test_imagecms.py | 2 +- test/test_imagecolor.py | 2 +- test/test_imagedraw.py | 2 +- test/test_imageenhance.py | 2 +- test/test_imagefileio.py | 2 +- test/test_imagefilter.py | 2 +- test/test_imagefont.py | 2 +- test/test_imagegrab.py | 2 +- test/test_imagemath.py | 2 +- test/test_imagemode.py | 2 +- test/test_imageops.py | 2 +- test/test_imageshow.py | 2 +- test/test_imagestat.py | 2 +- test/test_imagetk.py | 2 +- test/test_imagetransform.py | 2 +- test/test_imagewin.py | 2 +- test/test_lib_image.py | 2 +- test/test_lib_pack.py | 2 +- test/test_locale.py | 2 +- 54 files changed, 54 insertions(+), 54 deletions(-) diff --git a/test/test_bmp_reference.py b/test/test_bmp_reference.py index ce621b38a..ed012e7c8 100644 --- a/test/test_bmp_reference.py +++ b/test/test_bmp_reference.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image import os diff --git a/test/test_file_fli.py b/test/test_file_fli.py index 3479a4271..dd22a58f9 100644 --- a/test/test_file_fli.py +++ b/test/test_file_fli.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_icns.py b/test/test_file_icns.py index d65af2ecf..9d838620b 100644 --- a/test/test_file_icns.py +++ b/test/test_file_icns.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_ico.py b/test/test_file_ico.py index 9e524561c..dc289e1d2 100644 --- a/test/test_file_ico.py +++ b/test/test_file_ico.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_psd.py b/test/test_file_psd.py index b30b95bff..de3d6f33d 100644 --- a/test/test_file_psd.py +++ b/test/test_file_psd.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_xbm.py b/test/test_file_xbm.py index 0a4f50a49..02aec70b1 100644 --- a/test/test_file_xbm.py +++ b/test/test_file_xbm.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_file_xpm.py b/test/test_file_xpm.py index c74232812..ecbb4137a 100644 --- a/test/test_file_xpm.py +++ b/test/test_file_xpm.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_font_bdf.py b/test/test_font_bdf.py index 9e6a38dd9..b141e6149 100644 --- a/test/test_font_bdf.py +++ b/test/test_font_bdf.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import FontFile, BdfFontFile diff --git a/test/test_format_lab.py b/test/test_format_lab.py index 36e84801e..53468db5f 100644 --- a/test/test_format_lab.py +++ b/test/test_format_lab.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_image_array.py b/test/test_image_array.py index 3e30019fe..a0f5f29e1 100644 --- a/test/test_image_array.py +++ b/test/test_image_array.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_copy.py b/test/test_image_copy.py index 7c668a464..a7882db94 100644 --- a/test/test_image_copy.py +++ b/test/test_image_copy.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_crop.py b/test/test_image_crop.py index bcb097e25..da93fe7c8 100644 --- a/test/test_image_crop.py +++ b/test/test_image_crop.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_filter.py b/test/test_image_filter.py index 373d48356..4a85b0a2e 100644 --- a/test/test_image_filter.py +++ b/test/test_image_filter.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageFilter diff --git a/test/test_image_frombytes.py b/test/test_image_frombytes.py index 92885f9a9..aad8046a1 100644 --- a/test/test_image_frombytes.py +++ b/test/test_image_frombytes.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_getbands.py b/test/test_image_getbands.py index 03ff3914b..e803abb02 100644 --- a/test/test_image_getbands.py +++ b/test/test_image_getbands.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_image_getbbox.py b/test/test_image_getbbox.py index 1f01bcee3..83a6a3dec 100644 --- a/test/test_image_getbbox.py +++ b/test/test_image_getbbox.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_getcolors.py b/test/test_image_getcolors.py index 13c10ad86..cfc827b28 100644 --- a/test/test_image_getcolors.py +++ b/test/test_image_getcolors.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImage(PillowTestCase): diff --git a/test/test_image_getextrema.py b/test/test_image_getextrema.py index 77521294e..af7f7698a 100644 --- a/test/test_image_getextrema.py +++ b/test/test_image_getextrema.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImageGetExtrema(PillowTestCase): diff --git a/test/test_image_getim.py b/test/test_image_getim.py index 47283d97b..d498d3923 100644 --- a/test/test_image_getim.py +++ b/test/test_image_getim.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena, py3 +from helper import unittest, PillowTestCase, lena, py3 class TestImageGetIm(PillowTestCase): diff --git a/test/test_image_getpalette.py b/test/test_image_getpalette.py index d9184412a..0c399c432 100644 --- a/test/test_image_getpalette.py +++ b/test/test_image_getpalette.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImageGetPalette(PillowTestCase): diff --git a/test/test_image_histogram.py b/test/test_image_histogram.py index 4a0981009..70f78a1fb 100644 --- a/test/test_image_histogram.py +++ b/test/test_image_histogram.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImageHistogram(PillowTestCase): diff --git a/test/test_image_load.py b/test/test_image_load.py index 9c6f09388..2001c233a 100644 --- a/test/test_image_load.py +++ b/test/test_image_load.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_mode.py b/test/test_image_mode.py index 2c1bac609..d229a27a2 100644 --- a/test/test_image_mode.py +++ b/test/test_image_mode.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_offset.py b/test/test_image_offset.py index 146e0af9c..bb9b5a38c 100644 --- a/test/test_image_offset.py +++ b/test/test_image_offset.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImage(PillowTestCase): diff --git a/test/test_image_putalpha.py b/test/test_image_putalpha.py index 0ea1215f9..85c7ac262 100644 --- a/test/test_image_putalpha.py +++ b/test/test_image_putalpha.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_image_putdata.py b/test/test_image_putdata.py index be8e6bc22..c7c3115aa 100644 --- a/test/test_image_putdata.py +++ b/test/test_image_putdata.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena import sys diff --git a/test/test_image_putpalette.py b/test/test_image_putpalette.py index d6daaa42b..b77dcbf00 100644 --- a/test/test_image_putpalette.py +++ b/test/test_image_putpalette.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import ImagePalette diff --git a/test/test_image_quantize.py b/test/test_image_quantize.py index c1bbdd134..35f876717 100644 --- a/test/test_image_quantize.py +++ b/test/test_image_quantize.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_image_resize.py b/test/test_image_resize.py index 62cd971c5..6c9932e45 100644 --- a/test/test_image_resize.py +++ b/test/test_image_resize.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImageResize(PillowTestCase): diff --git a/test/test_image_rotate.py b/test/test_image_rotate.py index e00b5a4c5..531fdd63f 100644 --- a/test/test_image_rotate.py +++ b/test/test_image_rotate.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImageRotate(PillowTestCase): diff --git a/test/test_image_thumbnail.py b/test/test_image_thumbnail.py index 1ab06d360..ee49be43e 100644 --- a/test/test_image_thumbnail.py +++ b/test/test_image_thumbnail.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena class TestImageThumbnail(PillowTestCase): diff --git a/test/test_image_tobitmap.py b/test/test_image_tobitmap.py index e7a66e712..4e2a16df0 100644 --- a/test/test_image_tobitmap.py +++ b/test/test_image_tobitmap.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena, fromstring +from helper import unittest, PillowTestCase, lena, fromstring class TestImage(PillowTestCase): diff --git a/test/test_image_tobytes.py b/test/test_image_tobytes.py index e1bfa29fd..3be9128c1 100644 --- a/test/test_image_tobytes.py +++ b/test/test_image_tobytes.py @@ -1,4 +1,4 @@ -from test.helper import unittest, lena +from helper import unittest, lena class TestImageToBytes(unittest.TestCase): diff --git a/test/test_image_transform.py b/test/test_image_transform.py index 1cf84c521..6ab186c12 100644 --- a/test/test_image_transform.py +++ b/test/test_image_transform.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_imagechops.py b/test/test_imagechops.py index 5ffc02feb..ec162d52f 100644 --- a/test/test_imagechops.py +++ b/test/test_imagechops.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageChops diff --git a/test/test_imagecms.py b/test/test_imagecms.py index 7e59184dd..1a31636e8 100644 --- a/test/test_imagecms.py +++ b/test/test_imagecms.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image diff --git a/test/test_imagecolor.py b/test/test_imagecolor.py index 2c41d0765..5d8944852 100644 --- a/test/test_imagecolor.py +++ b/test/test_imagecolor.py @@ -1,4 +1,4 @@ -from test.helper import unittest, lena +from helper import unittest, PillowTestCase from PIL import Image from PIL import ImageColor diff --git a/test/test_imagedraw.py b/test/test_imagedraw.py index 9d5d575c5..98876296f 100644 --- a/test/test_imagedraw.py +++ b/test/test_imagedraw.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageColor diff --git a/test/test_imageenhance.py b/test/test_imageenhance.py index d05f94655..433c49cf6 100644 --- a/test/test_imageenhance.py +++ b/test/test_imageenhance.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageEnhance diff --git a/test/test_imagefileio.py b/test/test_imagefileio.py index 4f96181ca..32ee0bc5e 100644 --- a/test/test_imagefileio.py +++ b/test/test_imagefileio.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena, tostring +from helper import unittest, PillowTestCase, lena, tostring from PIL import Image from PIL import ImageFileIO diff --git a/test/test_imagefilter.py b/test/test_imagefilter.py index 19bd1a874..f7edb409a 100644 --- a/test/test_imagefilter.py +++ b/test/test_imagefilter.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import ImageFilter diff --git a/test/test_imagefont.py b/test/test_imagefont.py index 2024ef2eb..17cb38cc2 100644 --- a/test/test_imagefont.py +++ b/test/test_imagefont.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image from PIL import ImageDraw diff --git a/test/test_imagegrab.py b/test/test_imagegrab.py index a3c7db9fc..2275d34a1 100644 --- a/test/test_imagegrab.py +++ b/test/test_imagegrab.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase try: from PIL import ImageGrab diff --git a/test/test_imagemath.py b/test/test_imagemath.py index 06b6dea08..17d43d25a 100644 --- a/test/test_imagemath.py +++ b/test/test_imagemath.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image from PIL import ImageMath diff --git a/test/test_imagemode.py b/test/test_imagemode.py index c8480ec3f..7fb596b46 100644 --- a/test/test_imagemode.py +++ b/test/test_imagemode.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import ImageMode diff --git a/test/test_imageops.py b/test/test_imageops.py index e904b06ce..a4a94ca4d 100644 --- a/test/test_imageops.py +++ b/test/test_imageops.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import ImageOps diff --git a/test/test_imageshow.py b/test/test_imageshow.py index 27af375e7..12594c98f 100644 --- a/test/test_imageshow.py +++ b/test/test_imageshow.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image from PIL import ImageShow diff --git a/test/test_imagestat.py b/test/test_imagestat.py index b15ee8000..4d30ff023 100644 --- a/test/test_imagestat.py +++ b/test/test_imagestat.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageStat diff --git a/test/test_imagetk.py b/test/test_imagetk.py index aa10df0f4..87a07e288 100644 --- a/test/test_imagetk.py +++ b/test/test_imagetk.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase class TestImageTk(PillowTestCase): diff --git a/test/test_imagetransform.py b/test/test_imagetransform.py index 5d7fc657f..f5741df32 100644 --- a/test/test_imagetransform.py +++ b/test/test_imagetransform.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image from PIL import ImageTransform diff --git a/test/test_imagewin.py b/test/test_imagewin.py index a3ca7ee98..916abc77b 100644 --- a/test/test_imagewin.py +++ b/test/test_imagewin.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image from PIL import ImageWin diff --git a/test/test_lib_image.py b/test/test_lib_image.py index 99d1aa9b0..e0a903b00 100644 --- a/test/test_lib_image.py +++ b/test/test_lib_image.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase from PIL import Image diff --git a/test/test_lib_pack.py b/test/test_lib_pack.py index d0162dbcb..102835b58 100644 --- a/test/test_lib_pack.py +++ b/test/test_lib_pack.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, py3 +from helper import unittest, PillowTestCase, py3 from PIL import Image diff --git a/test/test_locale.py b/test/test_locale.py index 77774dc31..599e46266 100644 --- a/test/test_locale.py +++ b/test/test_locale.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase, lena +from helper import unittest, PillowTestCase, lena from PIL import Image From e7bd48fa5ee8299cfa74355d0b98ac178a336ff1 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 9 Jun 2014 20:56:32 -0400 Subject: [PATCH 138/168] Revert "Experimental import fix" This reverts commit 7a10f6b39a2c1f191c977571e69ceafe8022c006. --- test/test_000_sanity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_000_sanity.py b/test/test_000_sanity.py index fda481ed1..22e582ec3 100644 --- a/test/test_000_sanity.py +++ b/test/test_000_sanity.py @@ -1,4 +1,4 @@ -from test.helper import unittest, PillowTestCase +from helper import unittest, PillowTestCase import PIL import PIL.Image From a78b713d88c0e9ed061ca44c85638fd44c4946aa Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 9 Jun 2014 20:56:34 -0400 Subject: [PATCH 139/168] Revert "Add test runner" This reverts commit b7f94e3609b027f108b75f89bf63f07e54262d7d. --- setup.py | 2 +- test/__init__.py | 0 test/helper.py | 3 --- 3 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 test/__init__.py diff --git a/setup.py b/setup.py index 45bddf937..83defd82b 100644 --- a/setup.py +++ b/setup.py @@ -680,7 +680,7 @@ setup( include_package_data=True, packages=find_packages(), scripts=glob.glob("Scripts/pil*.py"), - test_suite='test.helper.TestSuite', + test_suite='PIL.tests', keywords=["Imaging",], license='Standard PIL License', zip_safe=True, diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/helper.py b/test/helper.py index 588b4f0ec..567fc3945 100644 --- a/test/helper.py +++ b/test/helper.py @@ -308,6 +308,3 @@ def lena(mode="RGB", cache={}): # # # _setup() - -TestLoader = unittest.TestLoader() -TestSuite = TestLoader.discover(start_dir='.') From e8ef927bacad29bebd48c44da6982a1f452f7326 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 10 Jun 2014 12:05:46 +0300 Subject: [PATCH 140/168] Remove test_001_archive.py as it doesn't do anything: ../pil-archive/* doesn't exist --- Tests/test_001_archive.py | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 Tests/test_001_archive.py diff --git a/Tests/test_001_archive.py b/Tests/test_001_archive.py deleted file mode 100644 index a914a6c4c..000000000 --- a/Tests/test_001_archive.py +++ /dev/null @@ -1,23 +0,0 @@ -import PIL -import PIL.Image - -import glob, os - -for file in glob.glob("../pil-archive/*"): - f, e = os.path.splitext(file) - if e in [".txt", ".ttf", ".otf", ".zip"]: - continue - try: - im = PIL.Image.open(file) - im.load() - except IOError as v: - print("-", "failed to open", file, "-", v) - else: - print("+", file, im.mode, im.size, im.format) - if e == ".exif": - try: - info = im._getexif() - except KeyError as v: - print("-", "failed to get exif info from", file, "-", v) - -print("ok") From c66f332679f83f13fdc7ae86d67578ca7c7ebbe8 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 10 Jun 2014 12:09:47 +0300 Subject: [PATCH 141/168] Remove empty tests --- Tests/test_image_paste.py | 5 ----- Tests/test_image_save.py | 5 ----- Tests/test_image_seek.py | 5 ----- Tests/test_image_show.py | 5 ----- Tests/test_image_tell.py | 5 ----- Tests/test_image_verify.py | 5 ----- 6 files changed, 30 deletions(-) delete mode 100644 Tests/test_image_paste.py delete mode 100644 Tests/test_image_save.py delete mode 100644 Tests/test_image_seek.py delete mode 100644 Tests/test_image_show.py delete mode 100644 Tests/test_image_tell.py delete mode 100644 Tests/test_image_verify.py diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_paste.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_save.py b/Tests/test_image_save.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_save.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_seek.py b/Tests/test_image_seek.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_seek.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_show.py b/Tests/test_image_show.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_show.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_tell.py b/Tests/test_image_tell.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_tell.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() diff --git a/Tests/test_image_verify.py b/Tests/test_image_verify.py deleted file mode 100644 index 7d4b6d9b3..000000000 --- a/Tests/test_image_verify.py +++ /dev/null @@ -1,5 +0,0 @@ -from tester import * - -from PIL import Image - -success() From 3ec505958ed6225c7d0d6481bf4a6bad5d2a3324 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 10 Jun 2014 12:10:47 +0300 Subject: [PATCH 142/168] Convert old tests to use unittest --- Tests/test_000_sanity.py | 40 ++- Tests/test_bmp_reference.py | 156 +++++----- Tests/test_cffi.py | 197 +++++++----- Tests/test_file_bmp.py | 64 ++-- Tests/test_file_eps.py | 201 +++++++------ Tests/test_file_fli.py | 23 +- Tests/test_file_gif.py | 147 ++++----- Tests/test_file_icns.py | 114 +++---- Tests/test_file_ico.py | 23 +- Tests/test_file_jpeg.py | 427 +++++++++++++------------- Tests/test_file_jpeg2k.py | 174 +++++------ Tests/test_file_libtiff.py | 484 ++++++++++++++++-------------- Tests/test_file_libtiff_small.py | 82 ++--- Tests/test_file_msp.py | 27 +- Tests/test_file_pcx.py | 67 +++-- Tests/test_file_pdf.py | 99 +++--- Tests/test_file_png.py | 356 +++++++++++----------- Tests/test_file_ppm.py | 54 ++-- Tests/test_file_psd.py | 23 +- Tests/test_file_spider.py | 53 ++-- Tests/test_file_tar.py | 46 +-- Tests/test_file_tiff.py | 245 +++++++-------- Tests/test_file_tiff_metadata.py | 137 +++++---- Tests/test_file_webp.py | 117 ++++---- Tests/test_file_webp_alpha.py | 143 ++++----- Tests/test_file_webp_lossless.py | 46 +-- Tests/test_file_webp_metadata.py | 201 +++++++------ Tests/test_file_xbm.py | 22 +- Tests/test_file_xpm.py | 23 +- Tests/test_font_bdf.py | 23 +- Tests/test_font_pcf.py | 76 +++-- Tests/test_format_lab.py | 63 ++-- Tests/test_image.py | 66 ++-- Tests/test_image_array.py | 67 +++-- Tests/test_image_convert.py | 238 +++++++-------- Tests/test_image_copy.py | 26 +- Tests/test_image_crop.py | 83 ++--- Tests/test_image_draft.py | 43 +-- Tests/test_image_filter.py | 139 +++++---- Tests/test_image_frombytes.py | 18 +- Tests/test_image_getbands.py | 33 +- Tests/test_image_getbbox.py | 55 ++-- Tests/test_image_getcolors.py | 106 ++++--- Tests/test_image_getdata.py | 47 +-- Tests/test_image_getextrema.py | 36 ++- Tests/test_image_getim.py | 21 +- Tests/test_image_getpalette.py | 41 +-- Tests/test_image_getpixel.py | 62 ++-- Tests/test_image_getprojection.py | 50 +-- Tests/test_image_histogram.py | 37 ++- Tests/test_image_load.py | 40 ++- Tests/test_image_mode.py | 53 ++-- Tests/test_image_offset.py | 29 +- Tests/test_image_point.py | 66 ++-- Tests/test_image_putalpha.py | 65 ++-- Tests/test_image_putdata.py | 62 ++-- Tests/test_image_putpalette.py | 56 ++-- Tests/test_image_putpixel.py | 79 ++--- Tests/test_image_quantize.py | 42 +-- Tests/test_image_resize.py | 27 +- Tests/test_image_rotate.py | 33 +- Tests/test_image_split.py | 102 ++++--- Tests/test_image_thumbnail.py | 57 ++-- Tests/test_image_tobitmap.py | 25 +- Tests/test_image_tobytes.py | 16 +- Tests/test_image_transform.py | 229 +++++++------- Tests/test_image_transpose.py | 42 ++- Tests/test_imagechops.py | 100 +++--- Tests/test_imagecms.py | 373 ++++++++++++----------- Tests/test_imagecolor.py | 101 ++++--- Tests/test_imagedraw.py | 359 +++++++++++----------- Tests/test_imageenhance.py | 31 +- Tests/test_imagefile.py | 125 ++++---- Tests/test_imagefileio.py | 37 ++- Tests/test_imagefilter.py | 52 ++-- Tests/test_imagefont.py | 258 ++++++++-------- Tests/test_imagegrab.py | 26 +- Tests/test_imagemath.py | 90 +++--- Tests/test_imagemode.py | 45 +-- Tests/test_imageops.py | 114 +++---- Tests/test_imageops_usm.py | 87 +++--- Tests/test_imagepalette.py | 52 ++-- Tests/test_imagepath.py | 96 +++--- Tests/test_imageqt.py | 72 +++-- Tests/test_imagesequence.py | 33 +- Tests/test_imageshow.py | 16 +- Tests/test_imagestat.py | 83 ++--- Tests/test_imagetk.py | 22 +- Tests/test_imagetransform.py | 33 +- Tests/test_imagewin.py | 16 +- Tests/test_lib_image.py | 55 ++-- Tests/test_lib_pack.py | 213 ++++++------- Tests/test_locale.py | 44 +-- Tests/test_mode_i16.py | 202 +++++++------ Tests/test_numpy.py | 206 +++++++------ Tests/test_olefileio.py | 295 +++++++++--------- Tests/test_pickle.py | 123 ++++---- 97 files changed, 5162 insertions(+), 4341 deletions(-) diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py index a30786458..1ad76cc50 100644 --- a/Tests/test_000_sanity.py +++ b/Tests/test_000_sanity.py @@ -1,24 +1,32 @@ -from __future__ import print_function -from tester import * +from helper import unittest, PillowTestCase, tearDownModule import PIL import PIL.Image -# Make sure we have the binary extension -im = PIL.Image.core.new("L", (100, 100)) -assert PIL.Image.VERSION[:3] == '1.1' +class TestSanity(PillowTestCase): -# Create an image and do stuff with it. -im = PIL.Image.new("1", (100, 100)) -assert (im.mode, im.size) == ('1', (100, 100)) -assert len(im.tobytes()) == 1300 + def test_sanity(self): -# Create images in all remaining major modes. -im = PIL.Image.new("L", (100, 100)) -im = PIL.Image.new("P", (100, 100)) -im = PIL.Image.new("RGB", (100, 100)) -im = PIL.Image.new("I", (100, 100)) -im = PIL.Image.new("F", (100, 100)) + # Make sure we have the binary extension + im = PIL.Image.core.new("L", (100, 100)) -print("ok") + self.assertEqual(PIL.Image.VERSION[:3], '1.1') + + # Create an image and do stuff with it. + im = PIL.Image.new("1", (100, 100)) + self.assertEqual((im.mode, im.size), ('1', (100, 100))) + self.assertEqual(len(im.tobytes()), 1300) + + # Create images in all remaining major modes. + im = PIL.Image.new("L", (100, 100)) + im = PIL.Image.new("P", (100, 100)) + im = PIL.Image.new("RGB", (100, 100)) + im = PIL.Image.new("I", (100, 100)) + im = PIL.Image.new("F", (100, 100)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index 99818229f..c8d93983b 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image import os @@ -6,81 +6,89 @@ import os base = os.path.join('Tests', 'images', 'bmp') -def get_files(d, ext='.bmp'): - return [os.path.join(base,d,f) for f - in os.listdir(os.path.join(base, d)) if ext in f] +class TestBmpReference(PillowTestCase): -def test_bad(): - """ These shouldn't crash/dos, but they shouldn't return anything either """ - for f in get_files('b'): - try: - im = Image.open(f) - im.load() - except Exception as msg: - pass - # print ("Bad Image %s: %s" %(f,msg)) + def get_files(self, d, ext='.bmp'): + return [os.path.join(base, d, f) for f + in os.listdir(os.path.join(base, d)) if ext in f] -def test_questionable(): - """ These shouldn't crash/dos, but its not well defined that these are in spec """ - for f in get_files('q'): - try: - im = Image.open(f) - im.load() - except Exception as msg: - pass - # print ("Bad Image %s: %s" %(f,msg)) + def test_bad(self): + """ These shouldn't crash/dos, but they shouldn't return anything + either """ + for f in self.get_files('b'): + try: + im = Image.open(f) + im.load() + except Exception: # as msg: + pass + # print ("Bad Image %s: %s" %(f,msg)) + + def test_questionable(self): + """ These shouldn't crash/dos, but its not well defined that these + are in spec """ + for f in self.get_files('q'): + try: + im = Image.open(f) + im.load() + except Exception: # as msg: + pass + # print ("Bad Image %s: %s" %(f,msg)) + + def test_good(self): + """ These should all work. There's a set of target files in the + html directory that we can compare against. """ + + # Target files, if they're not just replacing the extension + file_map = {'pal1wb.bmp': 'pal1.png', + 'pal4rle.bmp': 'pal4.png', + 'pal8-0.bmp': 'pal8.png', + 'pal8rle.bmp': 'pal8.png', + 'pal8topdown.bmp': 'pal8.png', + 'pal8nonsquare.bmp': 'pal8nonsquare-v.png', + 'pal8os2.bmp': 'pal8.png', + 'pal8os2sp.bmp': 'pal8.png', + 'pal8os2v2.bmp': 'pal8.png', + 'pal8os2v2-16.bmp': 'pal8.png', + 'pal8v4.bmp': 'pal8.png', + 'pal8v5.bmp': 'pal8.png', + 'rgb16-565pal.bmp': 'rgb16-565.png', + 'rgb24pal.bmp': 'rgb24.png', + 'rgb32.bmp': 'rgb24.png', + 'rgb32bf.bmp': 'rgb24.png' + } + + def get_compare(f): + (head, name) = os.path.split(f) + if name in file_map: + return os.path.join(base, 'html', file_map[name]) + (name, ext) = os.path.splitext(name) + return os.path.join(base, 'html', "%s.png" % name) + + for f in self.get_files('g'): + try: + im = Image.open(f) + im.load() + compare = Image.open(get_compare(f)) + compare.load() + if im.mode == 'P': + # assert image similar doesn't really work + # with paletized image, since the palette might + # be differently ordered for an equivalent image. + im = im.convert('RGBA') + compare = im.convert('RGBA') + self.assert_image_similar(im, compare, 5) + + except Exception as msg: + # there are three here that are unsupported: + unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), + os.path.join(base, 'g', 'pal8rle.bmp'), + os.path.join(base, 'g', 'pal4rle.bmp')) + if f not in unsupported: + self.assertTrue( + False, "Unsupported Image %s: %s" % (f, msg)) -def test_good(): - """ These should all work. There's a set of target files in the - html directory that we can compare against. """ - - # Target files, if they're not just replacing the extension - file_map = { 'pal1wb.bmp': 'pal1.png', - 'pal4rle.bmp': 'pal4.png', - 'pal8-0.bmp': 'pal8.png', - 'pal8rle.bmp': 'pal8.png', - 'pal8topdown.bmp': 'pal8.png', - 'pal8nonsquare.bmp': 'pal8nonsquare-v.png', - 'pal8os2.bmp': 'pal8.png', - 'pal8os2sp.bmp': 'pal8.png', - 'pal8os2v2.bmp': 'pal8.png', - 'pal8os2v2-16.bmp': 'pal8.png', - 'pal8v4.bmp': 'pal8.png', - 'pal8v5.bmp': 'pal8.png', - 'rgb16-565pal.bmp': 'rgb16-565.png', - 'rgb24pal.bmp': 'rgb24.png', - 'rgb32.bmp': 'rgb24.png', - 'rgb32bf.bmp': 'rgb24.png' - } - - def get_compare(f): - (head, name) = os.path.split(f) - if name in file_map: - return os.path.join(base, 'html', file_map[name]) - (name,ext) = os.path.splitext(name) - return os.path.join(base, 'html', "%s.png"%name) - - for f in get_files('g'): - try: - im = Image.open(f) - im.load() - compare = Image.open(get_compare(f)) - compare.load() - if im.mode == 'P': - # assert image similar doesn't really work - # with paletized image, since the palette might - # be differently ordered for an equivalent image. - im = im.convert('RGBA') - compare = im.convert('RGBA') - assert_image_similar(im, compare,5) - - - except Exception as msg: - # there are three here that are unsupported: - unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), - os.path.join(base, 'g', 'pal8rle.bmp'), - os.path.join(base, 'g', 'pal4rle.bmp')) - if f not in unsupported: - assert_true(False, "Unsupported Image %s: %s" %(f,msg)) +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_cffi.py b/Tests/test_cffi.py index 1c0d8d31e..8ff4e817f 100644 --- a/Tests/test_cffi.py +++ b/Tests/test_cffi.py @@ -1,99 +1,136 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena try: import cffi + from PIL import PyAccess except: - skip() - -from PIL import Image, PyAccess + # Skip in setUp() + pass -import test_image_putpixel as put -import test_image_getpixel as get +from PIL import Image +from test_image_putpixel import TestImagePutPixel +from test_image_getpixel import TestImageGetPixel Image.USE_CFFI_ACCESS = True -def test_put(): - put.test_sanity() -def test_get(): - get.test_basic() - get.test_signedness() +class TestCffiPutPixel(TestImagePutPixel): -def _test_get_access(im): - """ Do we get the same thing as the old pixel access """ + def setUp(self): + try: + import cffi + except: + self.skipTest("No cffi") - """ Using private interfaces, forcing a capi access and a pyaccess for the same image """ - caccess = im.im.pixel_access(False) - access = PyAccess.new(im, False) - - w,h = im.size - for x in range(0,w,10): - for y in range(0,h,10): - assert_equal(access[(x,y)], caccess[(x,y)]) - -def test_get_vs_c(): - _test_get_access(lena('RGB')) - _test_get_access(lena('RGBA')) - _test_get_access(lena('L')) - _test_get_access(lena('LA')) - _test_get_access(lena('1')) - _test_get_access(lena('P')) - #_test_get_access(lena('PA')) # PA -- how do I make a PA image??? - _test_get_access(lena('F')) - - im = Image.new('I;16', (10,10), 40000) - _test_get_access(im) - im = Image.new('I;16L', (10,10), 40000) - _test_get_access(im) - im = Image.new('I;16B', (10,10), 40000) - _test_get_access(im) - - im = Image.new('I', (10,10), 40000) - _test_get_access(im) - # These don't actually appear to be modes that I can actually make, - # as unpack sets them directly into the I mode. - #im = Image.new('I;32L', (10,10), -2**10) - #_test_get_access(im) - #im = Image.new('I;32B', (10,10), 2**10) - #_test_get_access(im) + def test_put(self): + self.test_sanity() +class TestCffiGetPixel(TestImageGetPixel): -def _test_set_access(im, color): - """ Are we writing the correct bits into the image? """ + def setUp(self): + try: + import cffi + except: + self.skipTest("No cffi") - """ Using private interfaces, forcing a capi access and a pyaccess for the same image """ - caccess = im.im.pixel_access(False) - access = PyAccess.new(im, False) + def test_get(self): + self.test_basic() + self.test_signedness() - w,h = im.size - for x in range(0,w,10): - for y in range(0,h,10): - access[(x,y)] = color - assert_equal(color, caccess[(x,y)]) -def test_set_vs_c(): - _test_set_access(lena('RGB'), (255, 128,0) ) - _test_set_access(lena('RGBA'), (255, 192, 128, 0)) - _test_set_access(lena('L'), 128) - _test_set_access(lena('LA'), (128,128)) - _test_set_access(lena('1'), 255) - _test_set_access(lena('P') , 128) - ##_test_set_access(i, (128,128)) #PA -- undone how to make - _test_set_access(lena('F'), 1024.0) - - im = Image.new('I;16', (10,10), 40000) - _test_set_access(im, 45000) - im = Image.new('I;16L', (10,10), 40000) - _test_set_access(im, 45000) - im = Image.new('I;16B', (10,10), 40000) - _test_set_access(im, 45000) - +class TestCffi(PillowTestCase): - im = Image.new('I', (10,10), 40000) - _test_set_access(im, 45000) -# im = Image.new('I;32L', (10,10), -(2**10)) -# _test_set_access(im, -(2**13)+1) - #im = Image.new('I;32B', (10,10), 2**10) - #_test_set_access(im, 2**13-1) + def setUp(self): + try: + import cffi + except: + self.skipTest("No cffi") + + def _test_get_access(self, im): + """ Do we get the same thing as the old pixel access """ + + """ Using private interfaces, forcing a capi access and + a pyaccess for the same image """ + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + w, h = im.size + for x in range(0, w, 10): + for y in range(0, h, 10): + self.assertEqual(access[(x, y)], caccess[(x, y)]) + + def test_get_vs_c(self): + rgb = lena('RGB') + rgb.load() + self._test_get_access(rgb) + self._test_get_access(lena('RGBA')) + self._test_get_access(lena('L')) + self._test_get_access(lena('LA')) + self._test_get_access(lena('1')) + self._test_get_access(lena('P')) + # self._test_get_access(lena('PA')) # PA -- how do I make a PA image? + self._test_get_access(lena('F')) + + im = Image.new('I;16', (10, 10), 40000) + self._test_get_access(im) + im = Image.new('I;16L', (10, 10), 40000) + self._test_get_access(im) + im = Image.new('I;16B', (10, 10), 40000) + self._test_get_access(im) + + im = Image.new('I', (10, 10), 40000) + self._test_get_access(im) + # These don't actually appear to be modes that I can actually make, + # as unpack sets them directly into the I mode. + # im = Image.new('I;32L', (10, 10), -2**10) + # self._test_get_access(im) + # im = Image.new('I;32B', (10, 10), 2**10) + # self._test_get_access(im) + + def _test_set_access(self, im, color): + """ Are we writing the correct bits into the image? """ + + """ Using private interfaces, forcing a capi access and + a pyaccess for the same image """ + caccess = im.im.pixel_access(False) + access = PyAccess.new(im, False) + + w, h = im.size + for x in range(0, w, 10): + for y in range(0, h, 10): + access[(x, y)] = color + self.assertEqual(color, caccess[(x, y)]) + + def test_set_vs_c(self): + rgb = lena('RGB') + rgb.load() + self._test_set_access(rgb, (255, 128, 0)) + self._test_set_access(lena('RGBA'), (255, 192, 128, 0)) + self._test_set_access(lena('L'), 128) + self._test_set_access(lena('LA'), (128, 128)) + self._test_set_access(lena('1'), 255) + self._test_set_access(lena('P'), 128) + # self._test_set_access(i, (128, 128)) #PA -- undone how to make + self._test_set_access(lena('F'), 1024.0) + + im = Image.new('I;16', (10, 10), 40000) + self._test_set_access(im, 45000) + im = Image.new('I;16L', (10, 10), 40000) + self._test_set_access(im, 45000) + im = Image.new('I;16B', (10, 10), 40000) + self._test_set_access(im, 45000) + + im = Image.new('I', (10, 10), 40000) + self._test_set_access(im, 45000) + # im = Image.new('I;32L', (10, 10), -(2**10)) + # self._test_set_access(im, -(2**13)+1) + # im = Image.new('I;32B', (10, 10), 2**10) + # self._test_set_access(im, 2**13-1) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index dd5f31fd2..29ddb9824 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -1,38 +1,44 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image import io -def roundtrip(im): - outfile = tempfile("temp.bmp") - im.save(outfile, 'BMP') +class TestFileBmp(PillowTestCase): - reloaded = Image.open(outfile) - reloaded.load() - assert_equal(im.mode, reloaded.mode) - assert_equal(im.size, reloaded.size) - assert_equal(reloaded.format, "BMP") + def roundtrip(self, im): + outfile = self.tempfile("temp.bmp") + + im.save(outfile, 'BMP') + + reloaded = Image.open(outfile) + reloaded.load() + self.assertEqual(im.mode, reloaded.mode) + self.assertEqual(im.size, reloaded.size) + self.assertEqual(reloaded.format, "BMP") + + def test_sanity(self): + self.roundtrip(lena()) + + self.roundtrip(lena("1")) + self.roundtrip(lena("L")) + self.roundtrip(lena("P")) + self.roundtrip(lena("RGB")) + + def test_save_to_bytes(self): + output = io.BytesIO() + im = lena() + im.save(output, "BMP") + + output.seek(0) + reloaded = Image.open(output) + + self.assertEqual(im.mode, reloaded.mode) + self.assertEqual(im.size, reloaded.size) + self.assertEqual(reloaded.format, "BMP") -def test_sanity(): - roundtrip(lena()) - - roundtrip(lena("1")) - roundtrip(lena("L")) - roundtrip(lena("P")) - roundtrip(lena("RGB")) +if __name__ == '__main__': + unittest.main() - -def test_save_to_bytes(): - output = io.BytesIO() - im = lena() - im.save(output, "BMP") - - output.seek(0) - reloaded = Image.open(output) - - assert_equal(im.mode, reloaded.mode) - assert_equal(im.size, reloaded.size) - assert_equal(reloaded.format, "BMP") - +# End of file diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 34ece8c8b..6a1a1b5e2 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -1,11 +1,8 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image, EpsImagePlugin import io -if not EpsImagePlugin.has_ghostscript(): - skip() - # Our two EPS test files (they are identical except for their bounding boxes) file1 = "Tests/images/zero_bb.eps" file2 = "Tests/images/non_zero_bb.eps" @@ -20,123 +17,127 @@ file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png" # EPS test files with binary preview file3 = "Tests/images/binary_preview_map.eps" -def test_sanity(): - # Regular scale - image1 = Image.open(file1) - image1.load() - assert_equal(image1.mode, "RGB") - assert_equal(image1.size, (460, 352)) - assert_equal(image1.format, "EPS") - image2 = Image.open(file2) - image2.load() - assert_equal(image2.mode, "RGB") - assert_equal(image2.size, (360, 252)) - assert_equal(image2.format, "EPS") +class TestFileEps(PillowTestCase): - # Double scale - image1_scale2 = Image.open(file1) - image1_scale2.load(scale=2) - assert_equal(image1_scale2.mode, "RGB") - assert_equal(image1_scale2.size, (920, 704)) - assert_equal(image1_scale2.format, "EPS") + def setUp(self): + if not EpsImagePlugin.has_ghostscript(): + self.skipTest("Ghostscript not available") - image2_scale2 = Image.open(file2) - image2_scale2.load(scale=2) - assert_equal(image2_scale2.mode, "RGB") - assert_equal(image2_scale2.size, (720, 504)) - assert_equal(image2_scale2.format, "EPS") + def test_sanity(self): + # Regular scale + image1 = Image.open(file1) + image1.load() + self.assertEqual(image1.mode, "RGB") + self.assertEqual(image1.size, (460, 352)) + self.assertEqual(image1.format, "EPS") + image2 = Image.open(file2) + image2.load() + self.assertEqual(image2.mode, "RGB") + self.assertEqual(image2.size, (360, 252)) + self.assertEqual(image2.format, "EPS") -def test_file_object(): - # issue 479 - image1 = Image.open(file1) - with open(tempfile('temp_file.eps'), 'wb') as fh: - image1.save(fh, 'EPS') + # Double scale + image1_scale2 = Image.open(file1) + image1_scale2.load(scale=2) + self.assertEqual(image1_scale2.mode, "RGB") + self.assertEqual(image1_scale2.size, (920, 704)) + self.assertEqual(image1_scale2.format, "EPS") + image2_scale2 = Image.open(file2) + image2_scale2.load(scale=2) + self.assertEqual(image2_scale2.mode, "RGB") + self.assertEqual(image2_scale2.size, (720, 504)) + self.assertEqual(image2_scale2.format, "EPS") -def test_iobase_object(): - # issue 479 - image1 = Image.open(file1) - with io.open(tempfile('temp_iobase.eps'), 'wb') as fh: - image1.save(fh, 'EPS') + def test_file_object(self): + # issue 479 + image1 = Image.open(file1) + with open(self.tempfile('temp_file.eps'), 'wb') as fh: + image1.save(fh, 'EPS') + def test_iobase_object(self): + # issue 479 + image1 = Image.open(file1) + with io.open(self.tempfile('temp_iobase.eps'), 'wb') as fh: + image1.save(fh, 'EPS') -def test_render_scale1(): - # We need png support for these render test - codecs = dir(Image.core) - if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - skip("zip/deflate support not available") + def test_render_scale1(self): + # We need png support for these render test + codecs = dir(Image.core) + if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + self.skipTest("zip/deflate support not available") - # Zero bounding box - image1_scale1 = Image.open(file1) - image1_scale1.load() - image1_scale1_compare = Image.open(file1_compare).convert("RGB") - image1_scale1_compare.load() - assert_image_similar(image1_scale1, image1_scale1_compare, 5) + # Zero bounding box + image1_scale1 = Image.open(file1) + image1_scale1.load() + image1_scale1_compare = Image.open(file1_compare).convert("RGB") + image1_scale1_compare.load() + self.assert_image_similar(image1_scale1, image1_scale1_compare, 5) - # Non-Zero bounding box - image2_scale1 = Image.open(file2) - image2_scale1.load() - image2_scale1_compare = Image.open(file2_compare).convert("RGB") - image2_scale1_compare.load() - assert_image_similar(image2_scale1, image2_scale1_compare, 10) + # Non-Zero bounding box + image2_scale1 = Image.open(file2) + image2_scale1.load() + image2_scale1_compare = Image.open(file2_compare).convert("RGB") + image2_scale1_compare.load() + self.assert_image_similar(image2_scale1, image2_scale1_compare, 10) + def test_render_scale2(self): + # We need png support for these render test + codecs = dir(Image.core) + if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + self.skipTest("zip/deflate support not available") -def test_render_scale2(): - # We need png support for these render test - codecs = dir(Image.core) - if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - skip("zip/deflate support not available") + # Zero bounding box + image1_scale2 = Image.open(file1) + image1_scale2.load(scale=2) + image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB") + image1_scale2_compare.load() + self.assert_image_similar(image1_scale2, image1_scale2_compare, 5) - # Zero bounding box - image1_scale2 = Image.open(file1) - image1_scale2.load(scale=2) - image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB") - image1_scale2_compare.load() - assert_image_similar(image1_scale2, image1_scale2_compare, 5) + # Non-Zero bounding box + image2_scale2 = Image.open(file2) + image2_scale2.load(scale=2) + image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB") + image2_scale2_compare.load() + self.assert_image_similar(image2_scale2, image2_scale2_compare, 10) - # Non-Zero bounding box - image2_scale2 = Image.open(file2) - image2_scale2.load(scale=2) - image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB") - image2_scale2_compare.load() - assert_image_similar(image2_scale2, image2_scale2_compare, 10) + def test_resize(self): + # Arrange + image1 = Image.open(file1) + image2 = Image.open(file2) + new_size = (100, 100) + # Act + image1 = image1.resize(new_size) + image2 = image2.resize(new_size) -def test_resize(): - # Arrange - image1 = Image.open(file1) - image2 = Image.open(file2) - new_size = (100, 100) + # Assert + self.assertEqual(image1.size, new_size) + self.assertEqual(image2.size, new_size) - # Act - image1 = image1.resize(new_size) - image2 = image2.resize(new_size) + def test_thumbnail(self): + # Issue #619 + # Arrange + image1 = Image.open(file1) + image2 = Image.open(file2) + new_size = (100, 100) - # Assert - assert_equal(image1.size, new_size) - assert_equal(image2.size, new_size) + # Act + image1.thumbnail(new_size) + image2.thumbnail(new_size) + # Assert + self.assertEqual(max(image1.size), max(new_size)) + self.assertEqual(max(image2.size), max(new_size)) -def test_thumbnail(): - # Issue #619 - # Arrange - image1 = Image.open(file1) - image2 = Image.open(file2) - new_size = (100, 100) + def test_read_binary_preview(self): + # Issue 302 + # open image with binary preview + Image.open(file3) - # Act - image1.thumbnail(new_size) - image2.thumbnail(new_size) - - # Assert - assert_equal(max(image1.size), max(new_size)) - assert_equal(max(image2.size), max(new_size)) - -def test_read_binary_preview(): - # Issue 302 - # open image with binary preview - image1 = Image.open(file3) +if __name__ == '__main__': + unittest.main() # End of file diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 4e06a732e..787365c14 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image @@ -6,9 +6,18 @@ from PIL import Image file = "Images/lena.fli" data = open(file, "rb").read() -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "P") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "FLI") + +class TestFileFli(PillowTestCase): + + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "P") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "FLI") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 566baa200..3aefbe9b3 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -1,87 +1,96 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image codecs = dir(Image.core) -if "gif_encoder" not in codecs or "gif_decoder" not in codecs: - skip("gif support not available") # can this happen? - # sample gif stream file = "Images/lena.gif" with open(file, "rb") as f: data = f.read() -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "P") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "GIF") -def test_optimize(): - def test(optimize): - im = Image.new("L", (1, 1), 0) - file = BytesIO() - im.save(file, "GIF", optimize=optimize) - return len(file.getvalue()) - assert_equal(test(0), 800) - assert_equal(test(1), 38) +class TestFileGif(PillowTestCase): -def test_roundtrip(): - out = tempfile('temp.gif') - im = lena() - im.save(out) - reread = Image.open(out) + def setUp(self): + if "gif_encoder" not in codecs or "gif_decoder" not in codecs: + self.skipTest("gif support not available") # can this happen? - assert_image_similar(reread.convert('RGB'), im, 50) + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "P") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "GIF") -def test_roundtrip2(): - #see https://github.com/python-pillow/Pillow/issues/403 - out = tempfile('temp.gif') - im = Image.open('Images/lena.gif') - im2 = im.copy() - im2.save(out) - reread = Image.open(out) + def test_optimize(self): + from io import BytesIO - assert_image_similar(reread.convert('RGB'), lena(), 50) + def test(optimize): + im = Image.new("L", (1, 1), 0) + file = BytesIO() + im.save(file, "GIF", optimize=optimize) + return len(file.getvalue()) + self.assertEqual(test(0), 800) + self.assertEqual(test(1), 38) + + def test_roundtrip(self): + out = self.tempfile('temp.gif') + im = lena() + im.save(out) + reread = Image.open(out) + + self.assert_image_similar(reread.convert('RGB'), im, 50) + + def test_roundtrip2(self): + # see https://github.com/python-pillow/Pillow/issues/403 + out = self.tempfile('temp.gif') + im = Image.open('Images/lena.gif') + im2 = im.copy() + im2.save(out) + reread = Image.open(out) + + self.assert_image_similar(reread.convert('RGB'), lena(), 50) + + def test_palette_handling(self): + # see https://github.com/python-pillow/Pillow/issues/513 + + im = Image.open('Images/lena.gif') + im = im.convert('RGB') + + im = im.resize((100, 100), Image.ANTIALIAS) + im2 = im.convert('P', palette=Image.ADAPTIVE, colors=256) + + f = self.tempfile('temp.gif') + im2.save(f, optimize=True) + + reloaded = Image.open(f) + + self.assert_image_similar(im, reloaded.convert('RGB'), 10) + + def test_palette_434(self): + # see https://github.com/python-pillow/Pillow/issues/434 + + def roundtrip(im, *args, **kwargs): + out = self.tempfile('temp.gif') + im.save(out, *args, **kwargs) + reloaded = Image.open(out) + + return [im, reloaded] + + orig = "Tests/images/test.colors.gif" + im = Image.open(orig) + + self.assert_image_equal(*roundtrip(im)) + self.assert_image_equal(*roundtrip(im, optimize=True)) + + im = im.convert("RGB") + # check automatic P conversion + reloaded = roundtrip(im)[1].convert('RGB') + self.assert_image_equal(im, reloaded) -def test_palette_handling(): - # see https://github.com/python-pillow/Pillow/issues/513 - - im = Image.open('Images/lena.gif') - im = im.convert('RGB') - - im = im.resize((100,100), Image.ANTIALIAS) - im2 = im.convert('P', palette=Image.ADAPTIVE, colors=256) - - f = tempfile('temp.gif') - im2.save(f, optimize=True) - - reloaded = Image.open(f) - - assert_image_similar(im, reloaded.convert('RGB'), 10) - -def test_palette_434(): - # see https://github.com/python-pillow/Pillow/issues/434 - - def roundtrip(im, *args, **kwargs): - out = tempfile('temp.gif') - im.save(out, *args, **kwargs) - reloaded = Image.open(out) - - return [im, reloaded] - - orig = "Tests/images/test.colors.gif" - im = Image.open(orig) - - assert_image_equal(*roundtrip(im)) - assert_image_equal(*roundtrip(im, optimize=True)) - - im = im.convert("RGB") - # check automatic P conversion - reloaded = roundtrip(im)[1].convert('RGB') - assert_image_equal(im, reloaded) - +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 3e31f8879..c307ec844 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image @@ -8,59 +8,67 @@ data = open(file, "rb").read() enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') -def test_sanity(): - # Loading this icon by default should result in the largest size - # (512x512@2x) being loaded - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGBA") - assert_equal(im.size, (1024, 1024)) - assert_equal(im.format, "ICNS") -def test_sizes(): - # Check that we can load all of the sizes, and that the final pixel - # dimensions are as expected - im = Image.open(file) - for w,h,r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open(file) - im2.size = (w, h, r) - im2.load() - assert_equal(im2.mode, 'RGBA') - assert_equal(im2.size, (wr, hr)) +class TestFileIcns(PillowTestCase): -def test_older_icon(): - # This icon was made with Icon Composer rather than iconutil; it still - # uses PNG rather than JP2, however (since it was made on 10.9). - im = Image.open('Tests/images/pillow2.icns') - for w,h,r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open('Tests/images/pillow2.icns') - im2.size = (w, h, r) - im2.load() - assert_equal(im2.mode, 'RGBA') - assert_equal(im2.size, (wr, hr)) + def test_sanity(self): + # Loading this icon by default should result in the largest size + # (512x512@2x) being loaded + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (1024, 1024)) + self.assertEqual(im.format, "ICNS") -def test_jp2_icon(): - # This icon was made by using Uli Kusterer's oldiconutil to replace - # the PNG images with JPEG 2000 ones. The advantage of doing this is - # that OS X 10.5 supports JPEG 2000 but not PNG; some commercial - # software therefore does just this. - - # (oldiconutil is here: https://github.com/uliwitness/oldiconutil) + def test_sizes(self): + # Check that we can load all of the sizes, and that the final pixel + # dimensions are as expected + im = Image.open(file) + for w, h, r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open(file) + im2.size = (w, h, r) + im2.load() + self.assertEqual(im2.mode, 'RGBA') + self.assertEqual(im2.size, (wr, hr)) - if not enable_jpeg2k: - return - - im = Image.open('Tests/images/pillow3.icns') - for w,h,r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open('Tests/images/pillow3.icns') - im2.size = (w, h, r) - im2.load() - assert_equal(im2.mode, 'RGBA') - assert_equal(im2.size, (wr, hr)) - + def test_older_icon(self): + # This icon was made with Icon Composer rather than iconutil; it still + # uses PNG rather than JP2, however (since it was made on 10.9). + im = Image.open('Tests/images/pillow2.icns') + for w, h, r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open('Tests/images/pillow2.icns') + im2.size = (w, h, r) + im2.load() + self.assertEqual(im2.mode, 'RGBA') + self.assertEqual(im2.size, (wr, hr)) + + def test_jp2_icon(self): + # This icon was made by using Uli Kusterer's oldiconutil to replace + # the PNG images with JPEG 2000 ones. The advantage of doing this is + # that OS X 10.5 supports JPEG 2000 but not PNG; some commercial + # software therefore does just this. + + # (oldiconutil is here: https://github.com/uliwitness/oldiconutil) + + if not enable_jpeg2k: + return + + im = Image.open('Tests/images/pillow3.icns') + for w, h, r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open('Tests/images/pillow3.icns') + im2.size = (w, h, r) + im2.load() + self.assertEqual(im2.mode, 'RGBA') + self.assertEqual(im2.size, (wr, hr)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index e0db34acc..5bad94a53 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image @@ -6,9 +6,18 @@ from PIL import Image file = "Images/lena.ico" data = open(file, "rb").read() -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGBA") - assert_equal(im.size, (16, 16)) - assert_equal(im.format, "ICO") + +class TestFileIco(PillowTestCase): + + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (16, 16)) + self.assertEqual(im.format, "ICO") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 43ed55969..c21910962 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -1,238 +1,231 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena, py3 import random +from io import BytesIO from PIL import Image from PIL import ImageFile codecs = dir(Image.core) -if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: - skip("jpeg support not available") - test_file = "Images/lena.jpg" -def roundtrip(im, **options): - out = BytesIO() - im.save(out, "JPEG", **options) - bytes = out.tell() - out.seek(0) - im = Image.open(out) - im.bytes = bytes # for testing only - return im +class TestFileJpeg(PillowTestCase): -# -------------------------------------------------------------------- + def setUp(self): + if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: + self.skipTest("jpeg support not available") + def roundtrip(self, im, **options): + out = BytesIO() + im.save(out, "JPEG", **options) + bytes = out.tell() + out.seek(0) + im = Image.open(out) + im.bytes = bytes # for testing only + return im -def test_sanity(): + def test_sanity(self): - # internal version number - assert_match(Image.core.jpeglib_version, "\d+\.\d+$") + # internal version number + self.assertRegexpMatches(Image.core.jpeglib_version, "\d+\.\d+$") - im = Image.open(test_file) - im.load() - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "JPEG") - - -# -------------------------------------------------------------------- - -def test_app(): - # Test APP/COM reader (@PIL135) - im = Image.open(test_file) - assert_equal(im.applist[0], - ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00")) - assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) - assert_equal(len(im.applist), 2) - - -def test_cmyk(): - # Test CMYK handling. Thanks to Tim and Charlie for test data, - # Michael for getting me to look one more time. - f = "Tests/images/pil_sample_cmyk.jpg" - im = Image.open(f) - # the source image has red pixels in the upper left corner. - c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] - assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) - # the opposite corner is black - c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] - assert_true(k > 0.9) - # roundtrip, and check again - im = roundtrip(im) - c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] - assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) - c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] - assert_true(k > 0.9) - - -def test_dpi(): - def test(xdpi, ydpi=None): im = Image.open(test_file) - im = roundtrip(im, dpi=(xdpi, ydpi or xdpi)) - return im.info.get("dpi") - assert_equal(test(72), (72, 72)) - assert_equal(test(300), (300, 300)) - assert_equal(test(100, 200), (100, 200)) - assert_equal(test(0), None) # square pixels + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "JPEG") + + def test_app(self): + # Test APP/COM reader (@PIL135) + im = Image.open(test_file) + self.assertEqual( + im.applist[0], + ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00")) + self.assertEqual(im.applist[1], ("COM", b"Python Imaging Library")) + self.assertEqual(len(im.applist), 2) + + def test_cmyk(self): + # Test CMYK handling. Thanks to Tim and Charlie for test data, + # Michael for getting me to look one more time. + f = "Tests/images/pil_sample_cmyk.jpg" + im = Image.open(f) + # the source image has red pixels in the upper left corner. + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + self.assertEqual(c, 0.0) + self.assertGreater(m, 0.8) + self.assertGreater(y, 0.8) + self.assertEqual(k, 0.0) + # the opposite corner is black + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] + self.assertGreater(k, 0.9) + # roundtrip, and check again + im = self.roundtrip(im) + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + self.assertEqual(c, 0.0) + self.assertGreater(m, 0.8) + self.assertGreater(y, 0.8) + self.assertEqual(k, 0.0) + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] + self.assertGreater(k, 0.9) + + def test_dpi(self): + def test(xdpi, ydpi=None): + im = Image.open(test_file) + im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) + return im.info.get("dpi") + self.assertEqual(test(72), (72, 72)) + self.assertEqual(test(300), (300, 300)) + self.assertEqual(test(100, 200), (100, 200)) + self.assertEqual(test(0), None) # square pixels + + def test_icc(self): + # Test ICC support + im1 = Image.open("Tests/images/rgb.jpg") + icc_profile = im1.info["icc_profile"] + self.assertEqual(len(icc_profile), 3144) + # Roundtrip via physical file. + f = self.tempfile("temp.jpg") + im1.save(f, icc_profile=icc_profile) + im2 = Image.open(f) + self.assertEqual(im2.info.get("icc_profile"), icc_profile) + # Roundtrip via memory buffer. + im1 = self.roundtrip(lena()) + im2 = self.roundtrip(lena(), icc_profile=icc_profile) + self.assert_image_equal(im1, im2) + self.assertFalse(im1.info.get("icc_profile")) + self.assertTrue(im2.info.get("icc_profile")) + + def test_icc_big(self): + # Make sure that the "extra" support handles large blocks + def test(n): + # The ICC APP marker can store 65519 bytes per marker, so + # using a 4-byte test code should allow us to detect out of + # order issues. + icc_profile = (b"Test"*int(n/4+1))[:n] + assert len(icc_profile) == n # sanity + im1 = self.roundtrip(lena(), icc_profile=icc_profile) + self.assertEqual(im1.info.get("icc_profile"), icc_profile or None) + test(0) + test(1) + test(3) + test(4) + test(5) + test(65533-14) # full JPEG marker block + test(65533-14+1) # full block plus one byte + test(ImageFile.MAXBLOCK) # full buffer block + test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte + test(ImageFile.MAXBLOCK*4+3) # large block + + def test_optimize(self): + im1 = self.roundtrip(lena()) + im2 = self.roundtrip(lena(), optimize=1) + self.assert_image_equal(im1, im2) + self.assertGreaterEqual(im1.bytes, im2.bytes) + + def test_optimize_large_buffer(self): + # https://github.com/python-pillow/Pillow/issues/148 + f = self.tempfile('temp.jpg') + # this requires ~ 1.5x Image.MAXBLOCK + im = Image.new("RGB", (4096, 4096), 0xff3333) + im.save(f, format="JPEG", optimize=True) + + def test_progressive(self): + im1 = self.roundtrip(lena()) + im2 = self.roundtrip(lena(), progressive=True) + self.assert_image_equal(im1, im2) + self.assertGreaterEqual(im1.bytes, im2.bytes) + + def test_progressive_large_buffer(self): + f = self.tempfile('temp.jpg') + # this requires ~ 1.5x Image.MAXBLOCK + im = Image.new("RGB", (4096, 4096), 0xff3333) + im.save(f, format="JPEG", progressive=True) + + def test_progressive_large_buffer_highest_quality(self): + f = self.tempfile('temp.jpg') + if py3: + a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3)) + else: + a = b''.join(chr(random.randint(0, 255)) for _ in range(256 * 256 * 3)) + im = Image.frombuffer("RGB", (256, 256), a, "raw", "RGB", 0, 1) + # this requires more bytes than pixels in the image + im.save(f, format="JPEG", progressive=True, quality=100) + + def test_large_exif(self): + # https://github.com/python-pillow/Pillow/issues/148 + f = self.tempfile('temp.jpg') + im = lena() + im.save(f, 'JPEG', quality=90, exif=b"1"*65532) + + def test_progressive_compat(self): + im1 = self.roundtrip(lena()) + im2 = self.roundtrip(lena(), progressive=1) + im3 = self.roundtrip(lena(), progression=1) # compatibility + self.assert_image_equal(im1, im2) + self.assert_image_equal(im1, im3) + self.assertFalse(im1.info.get("progressive")) + self.assertFalse(im1.info.get("progression")) + self.assertTrue(im2.info.get("progressive")) + self.assertTrue(im2.info.get("progression")) + self.assertTrue(im3.info.get("progressive")) + self.assertTrue(im3.info.get("progression")) + + def test_quality(self): + im1 = self.roundtrip(lena()) + im2 = self.roundtrip(lena(), quality=50) + self.assert_image(im1, im2.mode, im2.size) + self.assertGreaterEqual(im1.bytes, im2.bytes) + + def test_smooth(self): + im1 = self.roundtrip(lena()) + im2 = self.roundtrip(lena(), smooth=100) + self.assert_image(im1, im2.mode, im2.size) + + def test_subsampling(self): + def getsampling(im): + layer = im.layer + return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] + # experimental API + im = self.roundtrip(lena(), subsampling=-1) # default + self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1)) + im = self.roundtrip(lena(), subsampling=0) # 4:4:4 + self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1)) + im = self.roundtrip(lena(), subsampling=1) # 4:2:2 + self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1)) + im = self.roundtrip(lena(), subsampling=2) # 4:1:1 + self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1)) + im = self.roundtrip(lena(), subsampling=3) # default (undefined) + self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1)) + + im = self.roundtrip(lena(), subsampling="4:4:4") + self.assertEqual(getsampling(im), (1, 1, 1, 1, 1, 1)) + im = self.roundtrip(lena(), subsampling="4:2:2") + self.assertEqual(getsampling(im), (2, 1, 1, 1, 1, 1)) + im = self.roundtrip(lena(), subsampling="4:1:1") + self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1)) + + self.assertRaises( + TypeError, lambda: self.roundtrip(lena(), subsampling="1:1:1")) + + def test_exif(self): + im = Image.open("Tests/images/pil_sample_rgb.jpg") + info = im._getexif() + self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh') + + def test_quality_keep(self): + im = Image.open("Images/lena.jpg") + f = self.tempfile('temp.jpg') + im.save(f, quality='keep') + + def test_junk_jpeg_header(self): + # https://github.com/python-pillow/Pillow/issues/630 + filename = "Tests/images/junk_jpeg_header.jpg" + Image.open(filename) -def test_icc(): - # Test ICC support - im1 = Image.open("Tests/images/rgb.jpg") - icc_profile = im1.info["icc_profile"] - assert_equal(len(icc_profile), 3144) - # Roundtrip via physical file. - f = tempfile("temp.jpg") - im1.save(f, icc_profile=icc_profile) - im2 = Image.open(f) - assert_equal(im2.info.get("icc_profile"), icc_profile) - # Roundtrip via memory buffer. - im1 = roundtrip(lena()) - im2 = roundtrip(lena(), icc_profile=icc_profile) - assert_image_equal(im1, im2) - assert_false(im1.info.get("icc_profile")) - assert_true(im2.info.get("icc_profile")) - - -def test_icc_big(): - # Make sure that the "extra" support handles large blocks - def test(n): - # The ICC APP marker can store 65519 bytes per marker, so - # using a 4-byte test code should allow us to detect out of - # order issues. - icc_profile = (b"Test"*int(n/4+1))[:n] - assert len(icc_profile) == n # sanity - im1 = roundtrip(lena(), icc_profile=icc_profile) - assert_equal(im1.info.get("icc_profile"), icc_profile or None) - test(0) - test(1) - test(3) - test(4) - test(5) - test(65533-14) # full JPEG marker block - test(65533-14+1) # full block plus one byte - test(ImageFile.MAXBLOCK) # full buffer block - test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte - test(ImageFile.MAXBLOCK*4+3) # large block - - -def test_optimize(): - im1 = roundtrip(lena()) - im2 = roundtrip(lena(), optimize=1) - assert_image_equal(im1, im2) - assert_true(im1.bytes >= im2.bytes) - - -def test_optimize_large_buffer(): - # https://github.com/python-pillow/Pillow/issues/148 - f = tempfile('temp.jpg') - # this requires ~ 1.5x Image.MAXBLOCK - im = Image.new("RGB", (4096, 4096), 0xff3333) - im.save(f, format="JPEG", optimize=True) - - -def test_progressive(): - im1 = roundtrip(lena()) - im2 = roundtrip(lena(), progressive=True) - assert_image_equal(im1, im2) - assert_true(im1.bytes >= im2.bytes) - - -def test_progressive_large_buffer(): - f = tempfile('temp.jpg') - # this requires ~ 1.5x Image.MAXBLOCK - im = Image.new("RGB", (4096, 4096), 0xff3333) - im.save(f, format="JPEG", progressive=True) - - -def test_progressive_large_buffer_highest_quality(): - f = tempfile('temp.jpg') - if py3: - a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3)) - else: - a = b''.join(chr(random.randint(0, 255)) for _ in range(256 * 256 * 3)) - im = Image.frombuffer("RGB", (256, 256), a, "raw", "RGB", 0, 1) - # this requires more bytes than pixels in the image - im.save(f, format="JPEG", progressive=True, quality=100) - - -def test_large_exif(): - # https://github.com/python-pillow/Pillow/issues/148 - f = tempfile('temp.jpg') - im = lena() - im.save(f, 'JPEG', quality=90, exif=b"1"*65532) - - -def test_progressive_compat(): - im1 = roundtrip(lena()) - im2 = roundtrip(lena(), progressive=1) - im3 = roundtrip(lena(), progression=1) # compatibility - assert_image_equal(im1, im2) - assert_image_equal(im1, im3) - assert_false(im1.info.get("progressive")) - assert_false(im1.info.get("progression")) - assert_true(im2.info.get("progressive")) - assert_true(im2.info.get("progression")) - assert_true(im3.info.get("progressive")) - assert_true(im3.info.get("progression")) - - -def test_quality(): - im1 = roundtrip(lena()) - im2 = roundtrip(lena(), quality=50) - assert_image(im1, im2.mode, im2.size) - assert_true(im1.bytes >= im2.bytes) - - -def test_smooth(): - im1 = roundtrip(lena()) - im2 = roundtrip(lena(), smooth=100) - assert_image(im1, im2.mode, im2.size) - - -def test_subsampling(): - def getsampling(im): - layer = im.layer - return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] - # experimental API - im = roundtrip(lena(), subsampling=-1) # default - assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=0) # 4:4:4 - assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=1) # 4:2:2 - assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=2) # 4:1:1 - assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling=3) # default (undefined) - assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) - - im = roundtrip(lena(), subsampling="4:4:4") - assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling="4:2:2") - assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) - im = roundtrip(lena(), subsampling="4:1:1") - assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) - - assert_exception(TypeError, lambda: roundtrip(lena(), subsampling="1:1:1")) - - -def test_exif(): - im = Image.open("Tests/images/pil_sample_rgb.jpg") - info = im._getexif() - assert_equal(info[305], 'Adobe Photoshop CS Macintosh') - - -def test_quality_keep(): - im = Image.open("Images/lena.jpg") - f = tempfile('temp.jpg') - assert_no_exception(lambda: im.save(f, quality='keep')) - - -def test_junk_jpeg_header(): - # https://github.com/python-pillow/Pillow/issues/630 - filename = "Tests/images/junk_jpeg_header.jpg" - assert_no_exception(lambda: Image.open(filename)) +if __name__ == '__main__': + unittest.main() # End of file diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index b11e5e6ab..9111f4f8a 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -1,110 +1,114 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image -from PIL import ImageFile +from io import BytesIO codecs = dir(Image.core) -if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs: - skip('JPEG 2000 support not available') - -# OpenJPEG 2.0.0 outputs this debugging message sometimes; we should -# ignore it---it doesn't represent a test failure. -ignore('Not enough memory to handle tile data') - test_card = Image.open('Tests/images/test-card.png') test_card.load() -def roundtrip(im, **options): - out = BytesIO() - im.save(out, "JPEG2000", **options) - bytes = out.tell() - out.seek(0) - im = Image.open(out) - im.bytes = bytes # for testing only - im.load() - return im +# OpenJPEG 2.0.0 outputs this debugging message sometimes; we should +# ignore it---it doesn't represent a test failure. +# 'Not enough memory to handle tile data' -# ---------------------------------------------------------------------- -def test_sanity(): - # Internal version number - assert_match(Image.core.jp2klib_version, '\d+\.\d+\.\d+$') +class TestFileJpeg2k(PillowTestCase): - im = Image.open('Tests/images/test-card-lossless.jp2') - im.load() - assert_equal(im.mode, 'RGB') - assert_equal(im.size, (640, 480)) - assert_equal(im.format, 'JPEG2000') - -# ---------------------------------------------------------------------- + def setUp(self): + if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs: + self.skipTest('JPEG 2000 support not available') -# These two test pre-written JPEG 2000 files that were not written with -# PIL (they were made using Adobe Photoshop) + def roundtrip(self, im, **options): + out = BytesIO() + im.save(out, "JPEG2000", **options) + bytes = out.tell() + out.seek(0) + im = Image.open(out) + im.bytes = bytes # for testing only + im.load() + return im -def test_lossless(): - im = Image.open('Tests/images/test-card-lossless.jp2') - im.load() - im.save('/tmp/test-card.png') - assert_image_similar(im, test_card, 1.0e-3) + def test_sanity(self): + # Internal version number + self.assertRegexpMatches(Image.core.jp2klib_version, '\d+\.\d+\.\d+$') -def test_lossy_tiled(): - im = Image.open('Tests/images/test-card-lossy-tiled.jp2') - im.load() - assert_image_similar(im, test_card, 2.0) + im = Image.open('Tests/images/test-card-lossless.jp2') + im.load() + self.assertEqual(im.mode, 'RGB') + self.assertEqual(im.size, (640, 480)) + self.assertEqual(im.format, 'JPEG2000') -# ---------------------------------------------------------------------- + # These two test pre-written JPEG 2000 files that were not written with + # PIL (they were made using Adobe Photoshop) -def test_lossless_rt(): - im = roundtrip(test_card) - assert_image_equal(im, test_card) + def test_lossless(self): + im = Image.open('Tests/images/test-card-lossless.jp2') + im.load() + im.save('/tmp/test-card.png') + self.assert_image_similar(im, test_card, 1.0e-3) -def test_lossy_rt(): - im = roundtrip(test_card, quality_layers=[20]) - assert_image_similar(im, test_card, 2.0) + def test_lossy_tiled(self): + im = Image.open('Tests/images/test-card-lossy-tiled.jp2') + im.load() + self.assert_image_similar(im, test_card, 2.0) -def test_tiled_rt(): - im = roundtrip(test_card, tile_size=(128, 128)) - assert_image_equal(im, test_card) + def test_lossless_rt(self): + im = self.roundtrip(test_card) + self.assert_image_equal(im, test_card) -def test_tiled_offset_rt(): - im = roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), - offset=(32, 32)) - assert_image_equal(im, test_card) - -def test_irreversible_rt(): - im = roundtrip(test_card, irreversible=True, quality_layers=[20]) - assert_image_similar(im, test_card, 2.0) + def test_lossy_rt(self): + im = self.roundtrip(test_card, quality_layers=[20]) + self.assert_image_similar(im, test_card, 2.0) -def test_prog_qual_rt(): - im = roundtrip(test_card, quality_layers=[60, 40, 20], progression='LRCP') - assert_image_similar(im, test_card, 2.0) + def test_tiled_rt(self): + im = self.roundtrip(test_card, tile_size=(128, 128)) + self.assert_image_equal(im, test_card) -def test_prog_res_rt(): - im = roundtrip(test_card, num_resolutions=8, progression='RLCP') - assert_image_equal(im, test_card) + def test_tiled_offset_rt(self): + im = self.roundtrip( + test_card, tile_size=(128, 128), + tile_offset=(0, 0), offset=(32, 32)) + self.assert_image_equal(im, test_card) -# ---------------------------------------------------------------------- + def test_irreversible_rt(self): + im = self.roundtrip(test_card, irreversible=True, quality_layers=[20]) + self.assert_image_similar(im, test_card, 2.0) -def test_reduce(): - im = Image.open('Tests/images/test-card-lossless.jp2') - im.reduce = 2 - im.load() - assert_equal(im.size, (160, 120)) + def test_prog_qual_rt(self): + im = self.roundtrip( + test_card, quality_layers=[60, 40, 20], progression='LRCP') + self.assert_image_similar(im, test_card, 2.0) -def test_layers(): - out = BytesIO() - test_card.save(out, 'JPEG2000', quality_layers=[100, 50, 10], - progression='LRCP') - out.seek(0) - - im = Image.open(out) - im.layers = 1 - im.load() - assert_image_similar(im, test_card, 13) + def test_prog_res_rt(self): + im = self.roundtrip(test_card, num_resolutions=8, progression='RLCP') + self.assert_image_equal(im, test_card) - out.seek(0) - im = Image.open(out) - im.layers = 3 - im.load() - assert_image_similar(im, test_card, 0.4) + def test_reduce(self): + im = Image.open('Tests/images/test-card-lossless.jp2') + im.reduce = 2 + im.load() + self.assertEqual(im.size, (160, 120)) + + def test_layers(self): + out = BytesIO() + test_card.save(out, 'JPEG2000', quality_layers=[100, 50, 10], + progression='LRCP') + out.seek(0) + + im = Image.open(out) + im.layers = 1 + im.load() + self.assert_image_similar(im, test_card, 13) + + out.seek(0) + im = Image.open(out) + im.layers = 3 + im.load() + self.assert_image_similar(im, test_card, 0.4) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index fddff443a..1afeee488 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1,300 +1,318 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena, py3 + import os from PIL import Image, TiffImagePlugin -codecs = dir(Image.core) -if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs: - skip("tiff support not available") +class TestFileLibTiff(PillowTestCase): -def _assert_noerr(im): - """Helper tests that assert basic sanity about the g4 tiff reading""" - #1 bit - assert_equal(im.mode, "1") + def setUp(self): + codecs = dir(Image.core) - # Does the data actually load - assert_no_exception(lambda: im.load()) - assert_no_exception(lambda: im.getdata()) + if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs: + self.skipTest("tiff support not available") - try: - assert_equal(im._compression, 'group4') - except: - print("No _compression") - print (dir(im)) + def _assert_noerr(self, im): + """Helper tests that assert basic sanity about the g4 tiff reading""" + # 1 bit + self.assertEqual(im.mode, "1") - # can we write it back out, in a different form. - out = tempfile("temp.png") - assert_no_exception(lambda: im.save(out)) + # Does the data actually load + im.load() + im.getdata() -def test_g4_tiff(): - """Test the ordinary file path load path""" + try: + self.assertEqual(im._compression, 'group4') + except: + print("No _compression") + print (dir(im)) - file = "Tests/images/lena_g4_500.tif" - im = Image.open(file) + # can we write it back out, in a different form. + out = self.tempfile("temp.png") + im.save(out) - assert_equal(im.size, (500,500)) - _assert_noerr(im) + def test_g4_tiff(self): + """Test the ordinary file path load path""" -def test_g4_large(): - file = "Tests/images/pport_g4.tif" - im = Image.open(file) - _assert_noerr(im) + file = "Tests/images/lena_g4_500.tif" + im = Image.open(file) -def test_g4_tiff_file(): - """Testing the string load path""" + self.assertEqual(im.size, (500, 500)) + self._assert_noerr(im) - file = "Tests/images/lena_g4_500.tif" - with open(file,'rb') as f: - im = Image.open(f) + def test_g4_large(self): + file = "Tests/images/pport_g4.tif" + im = Image.open(file) + self._assert_noerr(im) - assert_equal(im.size, (500,500)) - _assert_noerr(im) + def test_g4_tiff_file(self): + """Testing the string load path""" -def test_g4_tiff_bytesio(): - """Testing the stringio loading code path""" - from io import BytesIO - file = "Tests/images/lena_g4_500.tif" - s = BytesIO() - with open(file,'rb') as f: - s.write(f.read()) - s.seek(0) - im = Image.open(s) + file = "Tests/images/lena_g4_500.tif" + with open(file, 'rb') as f: + im = Image.open(f) - assert_equal(im.size, (500,500)) - _assert_noerr(im) + self.assertEqual(im.size, (500, 500)) + self._assert_noerr(im) -def test_g4_eq_png(): - """ Checking that we're actually getting the data that we expect""" - png = Image.open('Tests/images/lena_bw_500.png') - g4 = Image.open('Tests/images/lena_g4_500.tif') + def test_g4_tiff_bytesio(self): + """Testing the stringio loading code path""" + from io import BytesIO + file = "Tests/images/lena_g4_500.tif" + s = BytesIO() + with open(file, 'rb') as f: + s.write(f.read()) + s.seek(0) + im = Image.open(s) - assert_image_equal(g4, png) + self.assertEqual(im.size, (500, 500)) + self._assert_noerr(im) -# see https://github.com/python-pillow/Pillow/issues/279 -def test_g4_fillorder_eq_png(): - """ Checking that we're actually getting the data that we expect""" - png = Image.open('Tests/images/g4-fillorder-test.png') - g4 = Image.open('Tests/images/g4-fillorder-test.tif') + def test_g4_eq_png(self): + """ Checking that we're actually getting the data that we expect""" + png = Image.open('Tests/images/lena_bw_500.png') + g4 = Image.open('Tests/images/lena_g4_500.tif') - assert_image_equal(g4, png) + self.assert_image_equal(g4, png) -def test_g4_write(): - """Checking to see that the saved image is the same as what we wrote""" - file = "Tests/images/lena_g4_500.tif" - orig = Image.open(file) + # see https://github.com/python-pillow/Pillow/issues/279 + def test_g4_fillorder_eq_png(self): + """ Checking that we're actually getting the data that we expect""" + png = Image.open('Tests/images/g4-fillorder-test.png') + g4 = Image.open('Tests/images/g4-fillorder-test.tif') - out = tempfile("temp.tif") - rot = orig.transpose(Image.ROTATE_90) - assert_equal(rot.size,(500,500)) - rot.save(out) + self.assert_image_equal(g4, png) - reread = Image.open(out) - assert_equal(reread.size,(500,500)) - _assert_noerr(reread) - assert_image_equal(reread, rot) - assert_equal(reread.info['compression'], 'group4') + def test_g4_write(self): + """Checking to see that the saved image is the same as what we wrote""" + file = "Tests/images/lena_g4_500.tif" + orig = Image.open(file) - assert_equal(reread.info['compression'], orig.info['compression']) + out = self.tempfile("temp.tif") + rot = orig.transpose(Image.ROTATE_90) + self.assertEqual(rot.size, (500, 500)) + rot.save(out) - assert_false(orig.tobytes() == reread.tobytes()) + reread = Image.open(out) + self.assertEqual(reread.size, (500, 500)) + self._assert_noerr(reread) + self.assert_image_equal(reread, rot) + self.assertEqual(reread.info['compression'], 'group4') -def test_adobe_deflate_tiff(): - file = "Tests/images/tiff_adobe_deflate.tif" - im = Image.open(file) + self.assertEqual(reread.info['compression'], orig.info['compression']) - assert_equal(im.mode, "RGB") - assert_equal(im.size, (278, 374)) - assert_equal(im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0)) - assert_no_exception(lambda: im.load()) + self.assertNotEqual(orig.tobytes(), reread.tobytes()) -def test_write_metadata(): - """ Test metadata writing through libtiff """ - img = Image.open('Tests/images/lena_g4.tif') - f = tempfile('temp.tiff') + def test_adobe_deflate_tiff(self): + file = "Tests/images/tiff_adobe_deflate.tif" + im = Image.open(file) - img.save(f, tiffinfo = img.tag) + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (278, 374)) + self.assertEqual( + im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0)) + im.load() - loaded = Image.open(f) + def test_write_metadata(self): + """ Test metadata writing through libtiff """ + img = Image.open('Tests/images/lena_g4.tif') + f = self.tempfile('temp.tiff') - original = img.tag.named() - reloaded = loaded.tag.named() + img.save(f, tiffinfo=img.tag) - # PhotometricInterpretation is set from SAVE_INFO, not the original image. - ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'PhotometricInterpretation'] + loaded = Image.open(f) - for tag, value in reloaded.items(): - if tag not in ignored: - if tag.endswith('Resolution'): - val = original[tag] - assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1], - msg="%s didn't roundtrip" % tag) - else: - assert_equal(original[tag], value, "%s didn't roundtrip" % tag) + original = img.tag.named() + reloaded = loaded.tag.named() - for tag, value in original.items(): - if tag not in ignored: - if tag.endswith('Resolution'): - val = reloaded[tag] - assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1], - msg="%s didn't roundtrip" % tag) - else: - assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag) + # PhotometricInterpretation is set from SAVE_INFO, + # not the original image. + ignored = [ + 'StripByteCounts', 'RowsPerStrip', + 'PageNumber', 'PhotometricInterpretation'] + for tag, value in reloaded.items(): + if tag not in ignored: + if tag.endswith('Resolution'): + val = original[tag] + self.assert_almost_equal( + val[0][0]/val[0][1], value[0][0]/value[0][1], + msg="%s didn't roundtrip" % tag) + else: + self.assertEqual( + original[tag], value, "%s didn't roundtrip" % tag) -def test_g3_compression(): - i = Image.open('Tests/images/lena_g4_500.tif') - out = tempfile("temp.tif") - i.save(out, compression='group3') + for tag, value in original.items(): + if tag not in ignored: + if tag.endswith('Resolution'): + val = reloaded[tag] + self.assert_almost_equal( + val[0][0]/val[0][1], value[0][0]/value[0][1], + msg="%s didn't roundtrip" % tag) + else: + self.assertEqual( + value, reloaded[tag], "%s didn't roundtrip" % tag) - reread = Image.open(out) - assert_equal(reread.info['compression'], 'group3') - assert_image_equal(reread, i) + def test_g3_compression(self): + i = Image.open('Tests/images/lena_g4_500.tif') + out = self.tempfile("temp.tif") + i.save(out, compression='group3') -def test_little_endian(): - im = Image.open('Tests/images/16bit.deflate.tif') - assert_equal(im.getpixel((0,0)), 480) - assert_equal(im.mode, 'I;16') + reread = Image.open(out) + self.assertEqual(reread.info['compression'], 'group3') + self.assert_image_equal(reread, i) - b = im.tobytes() - # Bytes are in image native order (little endian) - if py3: - assert_equal(b[0], ord(b'\xe0')) - assert_equal(b[1], ord(b'\x01')) - else: - assert_equal(b[0], b'\xe0') - assert_equal(b[1], b'\x01') + def test_little_endian(self): + im = Image.open('Tests/images/16bit.deflate.tif') + self.assertEqual(im.getpixel((0, 0)), 480) + self.assertEqual(im.mode, 'I;16') + b = im.tobytes() + # Bytes are in image native order (little endian) + if py3: + self.assertEqual(b[0], ord(b'\xe0')) + self.assertEqual(b[1], ord(b'\x01')) + else: + self.assertEqual(b[0], b'\xe0') + self.assertEqual(b[1], b'\x01') - out = tempfile("temp.tif") - #out = "temp.le.tif" - im.save(out) - reread = Image.open(out) + out = self.tempfile("temp.tif") + # out = "temp.le.tif" + im.save(out) + reread = Image.open(out) - assert_equal(reread.info['compression'], im.info['compression']) - assert_equal(reread.getpixel((0,0)), 480) - # UNDONE - libtiff defaults to writing in native endian, so - # on big endian, we'll get back mode = 'I;16B' here. + self.assertEqual(reread.info['compression'], im.info['compression']) + self.assertEqual(reread.getpixel((0, 0)), 480) + # UNDONE - libtiff defaults to writing in native endian, so + # on big endian, we'll get back mode = 'I;16B' here. -def test_big_endian(): - im = Image.open('Tests/images/16bit.MM.deflate.tif') + def test_big_endian(self): + im = Image.open('Tests/images/16bit.MM.deflate.tif') - assert_equal(im.getpixel((0,0)), 480) - assert_equal(im.mode, 'I;16B') + self.assertEqual(im.getpixel((0, 0)), 480) + self.assertEqual(im.mode, 'I;16B') - b = im.tobytes() + b = im.tobytes() - # Bytes are in image native order (big endian) - if py3: - assert_equal(b[0], ord(b'\x01')) - assert_equal(b[1], ord(b'\xe0')) - else: - assert_equal(b[0], b'\x01') - assert_equal(b[1], b'\xe0') + # Bytes are in image native order (big endian) + if py3: + self.assertEqual(b[0], ord(b'\x01')) + self.assertEqual(b[1], ord(b'\xe0')) + else: + self.assertEqual(b[0], b'\x01') + self.assertEqual(b[1], b'\xe0') - out = tempfile("temp.tif") - im.save(out) - reread = Image.open(out) + out = self.tempfile("temp.tif") + im.save(out) + reread = Image.open(out) - assert_equal(reread.info['compression'], im.info['compression']) - assert_equal(reread.getpixel((0,0)), 480) + self.assertEqual(reread.info['compression'], im.info['compression']) + self.assertEqual(reread.getpixel((0, 0)), 480) -def test_g4_string_info(): - """Tests String data in info directory""" - file = "Tests/images/lena_g4_500.tif" - orig = Image.open(file) + def test_g4_string_info(self): + """Tests String data in info directory""" + file = "Tests/images/lena_g4_500.tif" + orig = Image.open(file) - out = tempfile("temp.tif") + out = self.tempfile("temp.tif") - orig.tag[269] = 'temp.tif' - orig.save(out) + orig.tag[269] = 'temp.tif' + orig.save(out) - reread = Image.open(out) - assert_equal('temp.tif', reread.tag[269]) + reread = Image.open(out) + self.assertEqual('temp.tif', reread.tag[269]) -def test_12bit_rawmode(): - """ Are we generating the same interpretation of the image as Imagemagick is? """ - TiffImagePlugin.READ_LIBTIFF = True - #Image.DEBUG = True - im = Image.open('Tests/images/12bit.cropped.tif') - im.load() - TiffImagePlugin.READ_LIBTIFF = False - # to make the target -- - # convert 12bit.cropped.tif -depth 16 tmp.tif - # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif - # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, - # so we need to unshift so that the integer values are the same. + def test_12bit_rawmode(self): + """ Are we generating the same interpretation + of the image as Imagemagick is? """ + TiffImagePlugin.READ_LIBTIFF = True + # Image.DEBUG = True + im = Image.open('Tests/images/12bit.cropped.tif') + im.load() + TiffImagePlugin.READ_LIBTIFF = False + # to make the target -- + # convert 12bit.cropped.tif -depth 16 tmp.tif + # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif + # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, + # so we need to unshift so that the integer values are the same. - im2 = Image.open('Tests/images/12in16bit.tif') + im2 = Image.open('Tests/images/12in16bit.tif') - if Image.DEBUG: - print (im.getpixel((0,0))) - print (im.getpixel((0,1))) - print (im.getpixel((0,2))) + if Image.DEBUG: + print (im.getpixel((0, 0))) + print (im.getpixel((0, 1))) + print (im.getpixel((0, 2))) - print (im2.getpixel((0,0))) - print (im2.getpixel((0,1))) - print (im2.getpixel((0,2))) + print (im2.getpixel((0, 0))) + print (im2.getpixel((0, 1))) + print (im2.getpixel((0, 2))) - assert_image_equal(im, im2) + self.assert_image_equal(im, im2) -def test_blur(): - # test case from irc, how to do blur on b/w image and save to compressed tif. - from PIL import ImageFilter - out = tempfile('temp.tif') - im = Image.open('Tests/images/pport_g4.tif') - im = im.convert('L') + def test_blur(self): + # test case from irc, how to do blur on b/w image + # and save to compressed tif. + from PIL import ImageFilter + out = self.tempfile('temp.tif') + im = Image.open('Tests/images/pport_g4.tif') + im = im.convert('L') - im=im.filter(ImageFilter.GaussianBlur(4)) - im.save(out, compression='tiff_adobe_deflate') + im = im.filter(ImageFilter.GaussianBlur(4)) + im.save(out, compression='tiff_adobe_deflate') - im2 = Image.open(out) - im2.load() - - assert_image_equal(im, im2) - - -def test_compressions(): - im = lena('RGB') - out = tempfile('temp.tif') - - for compression in ('packbits', 'tiff_lzw'): - im.save(out, compression=compression) im2 = Image.open(out) - assert_image_equal(im, im2) + im2.load() - im.save(out, compression='jpeg') - im2 = Image.open(out) - assert_image_similar(im, im2, 30) + self.assert_image_equal(im, im2) + + def test_compressions(self): + im = lena('RGB') + out = self.tempfile('temp.tif') + + for compression in ('packbits', 'tiff_lzw'): + im.save(out, compression=compression) + im2 = Image.open(out) + self.assert_image_equal(im, im2) + + im.save(out, compression='jpeg') + im2 = Image.open(out) + self.assert_image_similar(im, im2, 30) + + def test_cmyk_save(self): + im = lena('CMYK') + out = self.tempfile('temp.tif') + + im.save(out, compression='tiff_adobe_deflate') + im2 = Image.open(out) + self.assert_image_equal(im, im2) + + def xtest_bw_compression_wRGB(self): + """ This test passes, but when running all tests causes a failure due + to output on stderr from the error thrown by libtiff. We need to + capture that but not now""" + + im = lena('RGB') + out = self.tempfile('temp.tif') + + self.assertRaises( + IOError, lambda: im.save(out, compression='tiff_ccitt')) + self.assertRaises(IOError, lambda: im.save(out, compression='group3')) + self.assertRaises(IOError, lambda: im.save(out, compression='group4')) + + def test_fp_leak(self): + im = Image.open("Tests/images/lena_g4_500.tif") + fn = im.fp.fileno() + + os.fstat(fn) + im.load() # this should close it. + self.assertRaises(OSError, lambda: os.fstat(fn)) + im = None # this should force even more closed. + self.assertRaises(OSError, lambda: os.fstat(fn)) + self.assertRaises(OSError, lambda: os.close(fn)) -def test_cmyk_save(): - im = lena('CMYK') - out = tempfile('temp.tif') +if __name__ == '__main__': + unittest.main() - im.save(out, compression='tiff_adobe_deflate') - im2 = Image.open(out) - assert_image_equal(im, im2) - -def xtest_bw_compression_wRGB(): - """ This test passes, but when running all tests causes a failure due to - output on stderr from the error thrown by libtiff. We need to capture that - but not now""" - - im = lena('RGB') - out = tempfile('temp.tif') - - assert_exception(IOError, lambda: im.save(out, compression='tiff_ccitt')) - assert_exception(IOError, lambda: im.save(out, compression='group3')) - assert_exception(IOError, lambda: im.save(out, compression='group4')) - -def test_fp_leak(): - im = Image.open("Tests/images/lena_g4_500.tif") - fn = im.fp.fileno() - - assert_no_exception(lambda: os.fstat(fn)) - im.load() # this should close it. - assert_exception(OSError, lambda: os.fstat(fn)) - im = None # this should force even more closed. - assert_exception(OSError, lambda: os.fstat(fn)) - assert_exception(OSError, lambda: os.close(fn)) +# End of file diff --git a/Tests/test_file_libtiff_small.py b/Tests/test_file_libtiff_small.py index 2ad71d6e6..acc2390c9 100644 --- a/Tests/test_file_libtiff_small.py +++ b/Tests/test_file_libtiff_small.py @@ -1,52 +1,56 @@ -from tester import * +from helper import unittest, tearDownModule from PIL import Image -from test_file_libtiff import _assert_noerr - -codecs = dir(Image.core) - -if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs: - skip("tiff support not available") - -""" The small lena image was failing on open in the libtiff - decoder because the file pointer was set to the wrong place - by a spurious seek. It wasn't failing with the byteio method. - - It was fixed by forcing an lseek to the beginning of the - file just before reading in libtiff. These tests remain - to ensure that it stays fixed. """ +from test_file_libtiff import TestFileLibTiff -def test_g4_lena_file(): - """Testing the open file load path""" +class TestFileLibTiffSmall(TestFileLibTiff): - file = "Tests/images/lena_g4.tif" - with open(file,'rb') as f: - im = Image.open(f) + # Inherits TestFileLibTiff's setUp() and self._assert_noerr() - assert_equal(im.size, (128,128)) - _assert_noerr(im) + """ The small lena image was failing on open in the libtiff + decoder because the file pointer was set to the wrong place + by a spurious seek. It wasn't failing with the byteio method. -def test_g4_lena_bytesio(): - """Testing the bytesio loading code path""" - from io import BytesIO - file = "Tests/images/lena_g4.tif" - s = BytesIO() - with open(file,'rb') as f: - s.write(f.read()) - s.seek(0) - im = Image.open(s) + It was fixed by forcing an lseek to the beginning of the + file just before reading in libtiff. These tests remain + to ensure that it stays fixed. """ - assert_equal(im.size, (128,128)) - _assert_noerr(im) + def test_g4_lena_file(self): + """Testing the open file load path""" -def test_g4_lena(): - """The 128x128 lena image fails for some reason. Investigating""" + file = "Tests/images/lena_g4.tif" + with open(file, 'rb') as f: + im = Image.open(f) - file = "Tests/images/lena_g4.tif" - im = Image.open(file) + self.assertEqual(im.size, (128, 128)) + self._assert_noerr(im) - assert_equal(im.size, (128,128)) - _assert_noerr(im) + def test_g4_lena_bytesio(self): + """Testing the bytesio loading code path""" + from io import BytesIO + file = "Tests/images/lena_g4.tif" + s = BytesIO() + with open(file, 'rb') as f: + s.write(f.read()) + s.seek(0) + im = Image.open(s) + self.assertEqual(im.size, (128, 128)) + self._assert_noerr(im) + + def test_g4_lena(self): + """The 128x128 lena image fails for some reason. Investigating""" + + file = "Tests/images/lena_g4.tif" + im = Image.open(file) + + self.assertEqual(im.size, (128, 128)) + self._assert_noerr(im) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py index 7ed200e43..2444879d1 100644 --- a/Tests/test_file_msp.py +++ b/Tests/test_file_msp.py @@ -1,15 +1,24 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image -def test_sanity(): - file = tempfile("temp.msp") +class TestFileMsp(PillowTestCase): - lena("1").save(file) + def test_sanity(self): - im = Image.open(file) - im.load() - assert_equal(im.mode, "1") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "MSP") + file = self.tempfile("temp.msp") + + lena("1").save(file) + + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "1") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "MSP") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index cb785ec54..d0800e203 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -1,40 +1,47 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image -def _roundtrip(im): - f = tempfile("temp.pcx") - im.save(f) - im2 = Image.open(f) +class TestFilePcx(PillowTestCase): - assert_equal(im2.mode, im.mode) - assert_equal(im2.size, im.size) - assert_equal(im2.format, "PCX") - assert_image_equal(im2, im) - -def test_sanity(): - for mode in ('1', 'L', 'P', 'RGB'): - _roundtrip(lena(mode)) + def _roundtrip(self, im): + f = self.tempfile("temp.pcx") + im.save(f) + im2 = Image.open(f) -def test_odd(): - # see issue #523, odd sized images should have a stride that's even. - # not that imagemagick or gimp write pcx that way. - # we were not handling properly. - for mode in ('1', 'L', 'P', 'RGB'): - # larger, odd sized images are better here to ensure that - # we handle interrupted scan lines properly. - _roundtrip(lena(mode).resize((511,511))) - + self.assertEqual(im2.mode, im.mode) + self.assertEqual(im2.size, im.size) + self.assertEqual(im2.format, "PCX") + self.assert_image_equal(im2, im) -def test_pil184(): - # Check reading of files where xmin/xmax is not zero. + def test_sanity(self): + for mode in ('1', 'L', 'P', 'RGB'): + self._roundtrip(lena(mode)) - file = "Tests/images/pil184.pcx" - im = Image.open(file) + def test_odd(self): + # see issue #523, odd sized images should have a stride that's even. + # not that imagemagick or gimp write pcx that way. + # we were not handling properly. + for mode in ('1', 'L', 'P', 'RGB'): + # larger, odd sized images are better here to ensure that + # we handle interrupted scan lines properly. + self._roundtrip(lena(mode).resize((511, 511))) - assert_equal(im.size, (447, 144)) - assert_equal(im.tile[0][1], (0, 0, 447, 144)) + def test_pil184(self): + # Check reading of files where xmin/xmax is not zero. - # Make sure all pixels are either 0 or 255. - assert_equal(im.histogram()[0] + im.histogram()[255], 447*144) + file = "Tests/images/pil184.pcx" + im = Image.open(file) + + self.assertEqual(im.size, (447, 144)) + self.assertEqual(im.tile[0][1], (0, 0, 447, 144)) + + # Make sure all pixels are either 0 or 255. + self.assertEqual(im.histogram()[0] + im.histogram()[255], 447*144) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index e99f22db1..089168393 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -1,58 +1,59 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena + import os.path -def helper_save_as_pdf(mode): - # Arrange - im = lena(mode) - outfile = tempfile("temp_" + mode + ".pdf") +class TestFilePdf(PillowTestCase): - # Act - im.save(outfile) + def helper_save_as_pdf(self, mode): + # Arrange + im = lena(mode) + outfile = self.tempfile("temp_" + mode + ".pdf") - # Assert - assert_true(os.path.isfile(outfile)) - assert_greater(os.path.getsize(outfile), 0) + # Act + im.save(outfile) + + # Assert + self.assertTrue(os.path.isfile(outfile)) + self.assertGreater(os.path.getsize(outfile), 0) + + def test_monochrome(self): + # Arrange + mode = "1" + + # Act / Assert + self.helper_save_as_pdf(mode) + + def test_greyscale(self): + # Arrange + mode = "L" + + # Act / Assert + self.helper_save_as_pdf(mode) + + def test_rgb(self): + # Arrange + mode = "RGB" + + # Act / Assert + self.helper_save_as_pdf(mode) + + def test_p_mode(self): + # Arrange + mode = "P" + + # Act / Assert + self.helper_save_as_pdf(mode) + + def test_cmyk_mode(self): + # Arrange + mode = "CMYK" + + # Act / Assert + self.helper_save_as_pdf(mode) -def test_monochrome(): - # Arrange - mode = "1" - - # Act / Assert - helper_save_as_pdf(mode) - - -def test_greyscale(): - # Arrange - mode = "L" - - # Act / Assert - helper_save_as_pdf(mode) - - -def test_rgb(): - # Arrange - mode = "RGB" - - # Act / Assert - helper_save_as_pdf(mode) - - -def test_p_mode(): - # Arrange - mode = "P" - - # Act / Assert - helper_save_as_pdf(mode) - - -def test_cmyk_mode(): - # Arrange - mode = "CMYK" - - # Act / Assert - helper_save_as_pdf(mode) - +if __name__ == '__main__': + unittest.main() # End of file diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 0b0ad3ca7..6da61be6b 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -1,4 +1,6 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena + +from io import BytesIO from PIL import Image from PIL import PngImagePlugin @@ -6,9 +8,6 @@ import zlib codecs = dir(Image.core) -if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - skip("zip/deflate support not available") - # sample png stream file = "Images/lena.png" @@ -18,6 +17,7 @@ data = open(file, "rb").read() MAGIC = PngImagePlugin._MAGIC + def chunk(cid, *data): file = BytesIO() PngImagePlugin.putchunk(*(file, cid) + data) @@ -32,256 +32,268 @@ IEND = chunk(b"IEND") HEAD = MAGIC + IHDR TAIL = IDAT + IEND + def load(data): return Image.open(BytesIO(data)) + def roundtrip(im, **options): out = BytesIO() im.save(out, "PNG", **options) out.seek(0) return Image.open(out) -# -------------------------------------------------------------------- -def test_sanity(): +class TestFilePng(PillowTestCase): - # internal version number - assert_match(Image.core.zlib_version, "\d+\.\d+\.\d+(\.\d+)?$") + def setUp(self): + if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + self.skipTest("zip/deflate support not available") - file = tempfile("temp.png") + def test_sanity(self): - lena("RGB").save(file) + # internal version number + self.assertRegexpMatches( + Image.core.zlib_version, "\d+\.\d+\.\d+(\.\d+)?$") - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "PNG") + file = self.tempfile("temp.png") - lena("1").save(file) - im = Image.open(file) + lena("RGB").save(file) - lena("L").save(file) - im = Image.open(file) + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "PNG") - lena("P").save(file) - im = Image.open(file) + lena("1").save(file) + im = Image.open(file) - lena("RGB").save(file) - im = Image.open(file) + lena("L").save(file) + im = Image.open(file) - lena("I").save(file) - im = Image.open(file) + lena("P").save(file) + im = Image.open(file) -# -------------------------------------------------------------------- + lena("RGB").save(file) + im = Image.open(file) -def test_broken(): - # Check reading of totally broken files. In this case, the test - # file was checked into Subversion as a text file. + lena("I").save(file) + im = Image.open(file) - file = "Tests/images/broken.png" - assert_exception(IOError, lambda: Image.open(file)) + def test_broken(self): + # Check reading of totally broken files. In this case, the test + # file was checked into Subversion as a text file. -def test_bad_text(): - # Make sure PIL can read malformed tEXt chunks (@PIL152) + file = "Tests/images/broken.png" + self.assertRaises(IOError, lambda: Image.open(file)) - im = load(HEAD + chunk(b'tEXt') + TAIL) - assert_equal(im.info, {}) + def test_bad_text(self): + # Make sure PIL can read malformed tEXt chunks (@PIL152) - im = load(HEAD + chunk(b'tEXt', b'spam') + TAIL) - assert_equal(im.info, {'spam': ''}) + im = load(HEAD + chunk(b'tEXt') + TAIL) + self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'tEXt', b'spam\0') + TAIL) - assert_equal(im.info, {'spam': ''}) + im = load(HEAD + chunk(b'tEXt', b'spam') + TAIL) + self.assertEqual(im.info, {'spam': ''}) - im = load(HEAD + chunk(b'tEXt', b'spam\0egg') + TAIL) - assert_equal(im.info, {'spam': 'egg'}) + im = load(HEAD + chunk(b'tEXt', b'spam\0') + TAIL) + self.assertEqual(im.info, {'spam': ''}) - im = load(HEAD + chunk(b'tEXt', b'spam\0egg\0') + TAIL) - assert_equal(im.info, {'spam': 'egg\x00'}) + im = load(HEAD + chunk(b'tEXt', b'spam\0egg') + TAIL) + self.assertEqual(im.info, {'spam': 'egg'}) -def test_bad_ztxt(): - # Test reading malformed zTXt chunks (python-pillow/Pillow#318) + im = load(HEAD + chunk(b'tEXt', b'spam\0egg\0') + TAIL) + self.assertEqual(im.info, {'spam': 'egg\x00'}) - im = load(HEAD + chunk(b'zTXt') + TAIL) - assert_equal(im.info, {}) + def test_bad_ztxt(self): + # Test reading malformed zTXt chunks (python-pillow/Pillow#318) - im = load(HEAD + chunk(b'zTXt', b'spam') + TAIL) - assert_equal(im.info, {'spam': ''}) + im = load(HEAD + chunk(b'zTXt') + TAIL) + self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'zTXt', b'spam\0') + TAIL) - assert_equal(im.info, {'spam': ''}) + im = load(HEAD + chunk(b'zTXt', b'spam') + TAIL) + self.assertEqual(im.info, {'spam': ''}) - im = load(HEAD + chunk(b'zTXt', b'spam\0\0') + TAIL) - assert_equal(im.info, {'spam': ''}) + im = load(HEAD + chunk(b'zTXt', b'spam\0') + TAIL) + self.assertEqual(im.info, {'spam': ''}) - im = load(HEAD + chunk(b'zTXt', b'spam\0\0' + zlib.compress(b'egg')[:1]) + TAIL) - assert_equal(im.info, {'spam': ''}) + im = load(HEAD + chunk(b'zTXt', b'spam\0\0') + TAIL) + self.assertEqual(im.info, {'spam': ''}) - im = load(HEAD + chunk(b'zTXt', b'spam\0\0' + zlib.compress(b'egg')) + TAIL) - assert_equal(im.info, {'spam': 'egg'}) + im = load(HEAD + chunk( + b'zTXt', b'spam\0\0' + zlib.compress(b'egg')[:1]) + TAIL) + self.assertEqual(im.info, {'spam': ''}) -def test_interlace(): + im = load( + HEAD + chunk(b'zTXt', b'spam\0\0' + zlib.compress(b'egg')) + TAIL) + self.assertEqual(im.info, {'spam': 'egg'}) - file = "Tests/images/pil123p.png" - im = Image.open(file) + def test_interlace(self): - assert_image(im, "P", (162, 150)) - assert_true(im.info.get("interlace")) + file = "Tests/images/pil123p.png" + im = Image.open(file) - assert_no_exception(lambda: im.load()) + self.assert_image(im, "P", (162, 150)) + self.assertTrue(im.info.get("interlace")) - file = "Tests/images/pil123rgba.png" - im = Image.open(file) + im.load() - assert_image(im, "RGBA", (162, 150)) - assert_true(im.info.get("interlace")) + file = "Tests/images/pil123rgba.png" + im = Image.open(file) - assert_no_exception(lambda: im.load()) + self.assert_image(im, "RGBA", (162, 150)) + self.assertTrue(im.info.get("interlace")) -def test_load_transparent_p(): - file = "Tests/images/pil123p.png" - im = Image.open(file) + im.load() - assert_image(im, "P", (162, 150)) - im = im.convert("RGBA") - assert_image(im, "RGBA", (162, 150)) + def test_load_transparent_p(self): + file = "Tests/images/pil123p.png" + im = Image.open(file) - # image has 124 uniqe qlpha values - assert_equal(len(im.split()[3].getcolors()), 124) + self.assert_image(im, "P", (162, 150)) + im = im.convert("RGBA") + self.assert_image(im, "RGBA", (162, 150)) -def test_load_transparent_rgb(): - file = "Tests/images/rgb_trns.png" - im = Image.open(file) + # image has 124 uniqe qlpha values + self.assertEqual(len(im.split()[3].getcolors()), 124) - assert_image(im, "RGB", (64, 64)) - im = im.convert("RGBA") - assert_image(im, "RGBA", (64, 64)) + def test_load_transparent_rgb(self): + file = "Tests/images/rgb_trns.png" + im = Image.open(file) - # image has 876 transparent pixels - assert_equal(im.split()[3].getcolors()[0][0], 876) + self.assert_image(im, "RGB", (64, 64)) + im = im.convert("RGBA") + self.assert_image(im, "RGBA", (64, 64)) -def test_save_p_transparent_palette(): - in_file = "Tests/images/pil123p.png" - im = Image.open(in_file) + # image has 876 transparent pixels + self.assertEqual(im.split()[3].getcolors()[0][0], 876) - file = tempfile("temp.png") - assert_no_exception(lambda: im.save(file)) + def test_save_p_transparent_palette(self): + in_file = "Tests/images/pil123p.png" + im = Image.open(in_file) -def test_save_p_single_transparency(): - in_file = "Tests/images/p_trns_single.png" - im = Image.open(in_file) + file = self.tempfile("temp.png") + im.save(file) - file = tempfile("temp.png") - assert_no_exception(lambda: im.save(file)) + def test_save_p_single_transparency(self): + in_file = "Tests/images/p_trns_single.png" + im = Image.open(in_file) -def test_save_l_transparency(): - in_file = "Tests/images/l_trns.png" - im = Image.open(in_file) + file = self.tempfile("temp.png") + im.save(file) - file = tempfile("temp.png") - assert_no_exception(lambda: im.save(file)) + def test_save_l_transparency(self): + in_file = "Tests/images/l_trns.png" + im = Image.open(in_file) - # There are 559 transparent pixels. - im = im.convert('RGBA') - assert_equal(im.split()[3].getcolors()[0][0], 559) + file = self.tempfile("temp.png") + im.save(file) -def test_save_rgb_single_transparency(): - in_file = "Tests/images/caption_6_33_22.png" - im = Image.open(in_file) + # There are 559 transparent pixels. + im = im.convert('RGBA') + self.assertEqual(im.split()[3].getcolors()[0][0], 559) - file = tempfile("temp.png") - assert_no_exception(lambda: im.save(file)) + def test_save_rgb_single_transparency(self): + in_file = "Tests/images/caption_6_33_22.png" + im = Image.open(in_file) -def test_load_verify(): - # Check open/load/verify exception (@PIL150) + file = self.tempfile("temp.png") + im.save(file) - im = Image.open("Images/lena.png") - assert_no_exception(lambda: im.verify()) + def test_load_verify(self): + # Check open/load/verify exception (@PIL150) - im = Image.open("Images/lena.png") - im.load() - assert_exception(RuntimeError, lambda: im.verify()) + im = Image.open("Images/lena.png") + im.verify() -def test_roundtrip_dpi(): - # Check dpi roundtripping + im = Image.open("Images/lena.png") + im.load() + self.assertRaises(RuntimeError, lambda: im.verify()) - im = Image.open(file) + def test_roundtrip_dpi(self): + # Check dpi roundtripping - im = roundtrip(im, dpi=(100, 100)) - assert_equal(im.info["dpi"], (100, 100)) + im = Image.open(file) -def test_roundtrip_text(): - # Check text roundtripping + im = roundtrip(im, dpi=(100, 100)) + self.assertEqual(im.info["dpi"], (100, 100)) - im = Image.open(file) + def test_roundtrip_text(self): + # Check text roundtripping - info = PngImagePlugin.PngInfo() - info.add_text("TXT", "VALUE") - info.add_text("ZIP", "VALUE", 1) + im = Image.open(file) - im = roundtrip(im, pnginfo=info) - assert_equal(im.info, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) - assert_equal(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + info = PngImagePlugin.PngInfo() + info.add_text("TXT", "VALUE") + info.add_text("ZIP", "VALUE", 1) -def test_scary(): - # Check reading of evil PNG file. For information, see: - # http://scary.beasts.org/security/CESA-2004-001.txt - # The first byte is removed from pngtest_bad.png - # to avoid classification as malware. + im = roundtrip(im, pnginfo=info) + self.assertEqual(im.info, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + self.assertEqual(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) - with open("Tests/images/pngtest_bad.png.bin", 'rb') as fd: - data = b'\x89' + fd.read() + def test_scary(self): + # Check reading of evil PNG file. For information, see: + # http://scary.beasts.org/security/CESA-2004-001.txt + # The first byte is removed from pngtest_bad.png + # to avoid classification as malware. - pngfile = BytesIO(data) - assert_exception(IOError, lambda: Image.open(pngfile)) + with open("Tests/images/pngtest_bad.png.bin", 'rb') as fd: + data = b'\x89' + fd.read() -def test_trns_rgb(): - # Check writing and reading of tRNS chunks for RGB images. - # Independent file sample provided by Sebastian Spaeth. + pngfile = BytesIO(data) + self.assertRaises(IOError, lambda: Image.open(pngfile)) - file = "Tests/images/caption_6_33_22.png" - im = Image.open(file) - assert_equal(im.info["transparency"], (248, 248, 248)) + def test_trns_rgb(self): + # Check writing and reading of tRNS chunks for RGB images. + # Independent file sample provided by Sebastian Spaeth. - # check saving transparency by default - im = roundtrip(im) - assert_equal(im.info["transparency"], (248, 248, 248)) + file = "Tests/images/caption_6_33_22.png" + im = Image.open(file) + self.assertEqual(im.info["transparency"], (248, 248, 248)) - im = roundtrip(im, transparency=(0, 1, 2)) - assert_equal(im.info["transparency"], (0, 1, 2)) + # check saving transparency by default + im = roundtrip(im) + self.assertEqual(im.info["transparency"], (248, 248, 248)) -def test_trns_p(): - # Check writing a transparency of 0, issue #528 - im = lena('P') - im.info['transparency']=0 + im = roundtrip(im, transparency=(0, 1, 2)) + self.assertEqual(im.info["transparency"], (0, 1, 2)) - f = tempfile("temp.png") - im.save(f) + def test_trns_p(self): + # Check writing a transparency of 0, issue #528 + im = lena('P') + im.info['transparency'] = 0 - im2 = Image.open(f) - assert_true('transparency' in im2.info) + f = self.tempfile("temp.png") + im.save(f) - assert_image_equal(im2.convert('RGBA'), im.convert('RGBA')) + im2 = Image.open(f) + self.assertIn('transparency', im2.info) + self.assert_image_equal(im2.convert('RGBA'), im.convert('RGBA')) -def test_save_icc_profile_none(): - # check saving files with an ICC profile set to None (omit profile) - in_file = "Tests/images/icc_profile_none.png" - im = Image.open(in_file) - assert_equal(im.info['icc_profile'], None) + def test_save_icc_profile_none(self): + # check saving files with an ICC profile set to None (omit profile) + in_file = "Tests/images/icc_profile_none.png" + im = Image.open(in_file) + self.assertEqual(im.info['icc_profile'], None) - im = roundtrip(im) - assert_false('icc_profile' in im.info) + im = roundtrip(im) + self.assertNotIn('icc_profile', im.info) -def test_roundtrip_icc_profile(): - # check that we can roundtrip the icc profile - im = lena('RGB') + def test_roundtrip_icc_profile(self): + # check that we can roundtrip the icc profile + im = lena('RGB') - jpeg_image = Image.open('Tests/images/flower2.jpg') - expected_icc = jpeg_image.info['icc_profile'] + jpeg_image = Image.open('Tests/images/flower2.jpg') + expected_icc = jpeg_image.info['icc_profile'] - im.info['icc_profile'] = expected_icc - im = roundtrip(im) - assert_equal(im.info['icc_profile'], expected_icc) + im.info['icc_profile'] = expected_icc + im = roundtrip(im) + self.assertEqual(im.info['icc_profile'], expected_icc) + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 34136a83c..9d5dd806a 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image @@ -6,31 +6,37 @@ from PIL import Image file = "Images/lena.ppm" data = open(file, "rb").read() -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "PPM") -def test_16bit_pgm(): - im = Image.open('Tests/images/16_bit_binary.pgm') - im.load() - assert_equal(im.mode, 'I') - assert_equal(im.size, (20,100)) +class TestFilePpm(PillowTestCase): - tgt = Image.open('Tests/images/16_bit_binary_pgm.png') - assert_image_equal(im, tgt) + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "PPM") + + def test_16bit_pgm(self): + im = Image.open('Tests/images/16_bit_binary.pgm') + im.load() + self.assertEqual(im.mode, 'I') + self.assertEqual(im.size, (20, 100)) + + tgt = Image.open('Tests/images/16_bit_binary_pgm.png') + self.assert_image_equal(im, tgt) + + def test_16bit_pgm_write(self): + im = Image.open('Tests/images/16_bit_binary.pgm') + im.load() + + f = self.tempfile('temp.pgm') + im.save(f, 'PPM') + + reloaded = Image.open(f) + self.assert_image_equal(im, reloaded) -def test_16bit_pgm_write(): - im = Image.open('Tests/images/16_bit_binary.pgm') - im.load() - - f = tempfile('temp.pgm') - assert_no_exception(lambda: im.save(f, 'PPM')) - - reloaded = Image.open(f) - assert_image_equal(im, reloaded) - +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index ef2d40594..e7f86fd98 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image @@ -6,9 +6,18 @@ from PIL import Image file = "Images/lena.psd" data = open(file, "rb").read() -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "PSD") + +class TestImagePsd(PillowTestCase): + + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "PSD") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index d93724492..e0731ca8c 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image from PIL import SpiderImagePlugin @@ -6,31 +6,34 @@ from PIL import SpiderImagePlugin test_file = "Tests/images/lena.spider" -def test_sanity(): - im = Image.open(test_file) - im.load() - assert_equal(im.mode, "F") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "SPIDER") +class TestImageSpider(PillowTestCase): + + def test_sanity(self): + im = Image.open(test_file) + im.load() + self.assertEqual(im.mode, "F") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "SPIDER") + + def test_save(self): + # Arrange + temp = self.tempfile('temp.spider') + im = lena() + + # Act + im.save(temp, "SPIDER") + + # Assert + im2 = Image.open(temp) + self.assertEqual(im2.mode, "F") + self.assertEqual(im2.size, (128, 128)) + self.assertEqual(im2.format, "SPIDER") + + def test_isSpiderImage(self): + self.assertTrue(SpiderImagePlugin.isSpiderImage(test_file)) -def test_save(): - # Arrange - temp = tempfile('temp.spider') - im = lena() - - # Act - im.save(temp, "SPIDER") - - # Assert - im2 = Image.open(temp) - assert_equal(im2.mode, "F") - assert_equal(im2.size, (128, 128)) - assert_equal(im2.format, "SPIDER") - - -def test_isSpiderImage(): - assert_true(SpiderImagePlugin.isSpiderImage(test_file)) - +if __name__ == '__main__': + unittest.main() # End of file diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py index fa33d3802..36a625add 100644 --- a/Tests/test_file_tar.py +++ b/Tests/test_file_tar.py @@ -1,28 +1,38 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image, TarIO codecs = dir(Image.core) -if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs: - skip("neither jpeg nor zip support not available") # sample ppm stream tarfile = "Images/lena.tar" -def test_sanity(): - if "zip_decoder" in codecs: - tar = TarIO.TarIO(tarfile, 'lena.png') - im = Image.open(tar) - im.load() - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "PNG") - if "jpeg_decoder" in codecs: - tar = TarIO.TarIO(tarfile, 'lena.jpg') - im = Image.open(tar) - im.load() - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "JPEG") +class TestFileTar(PillowTestCase): + def setUp(self): + if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs: + self.skipTest("neither jpeg nor zip support not available") + + def test_sanity(self): + if "zip_decoder" in codecs: + tar = TarIO.TarIO(tarfile, 'lena.png') + im = Image.open(tar) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "PNG") + + if "jpeg_decoder" in codecs: + tar = TarIO.TarIO(tarfile, 'lena.jpg') + im = Image.open(tar) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "JPEG") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 804ae04e4..ed3d1e9cd 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -1,141 +1,148 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena, py3 from PIL import Image -def test_sanity(): - file = tempfile("temp.tif") +class TestFileTiff(PillowTestCase): - lena("RGB").save(file) + def test_sanity(self): - im = Image.open(file) - im.load() - assert_equal(im.mode, "RGB") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "TIFF") + file = self.tempfile("temp.tif") - lena("1").save(file) - im = Image.open(file) + lena("RGB").save(file) - lena("L").save(file) - im = Image.open(file) + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "TIFF") - lena("P").save(file) - im = Image.open(file) + lena("1").save(file) + im = Image.open(file) - lena("RGB").save(file) - im = Image.open(file) + lena("L").save(file) + im = Image.open(file) - lena("I").save(file) - im = Image.open(file) + lena("P").save(file) + im = Image.open(file) -def test_mac_tiff(): - # Read RGBa images from Mac OS X [@PIL136] + lena("RGB").save(file) + im = Image.open(file) - file = "Tests/images/pil136.tiff" - im = Image.open(file) + lena("I").save(file) + im = Image.open(file) - assert_equal(im.mode, "RGBA") - assert_equal(im.size, (55, 43)) - assert_equal(im.tile, [('raw', (0, 0, 55, 43), 8, ('RGBa', 0, 1))]) - assert_no_exception(lambda: im.load()) + def test_mac_tiff(self): + # Read RGBa images from Mac OS X [@PIL136] -def test_gimp_tiff(): - # Read TIFF JPEG images from GIMP [@PIL168] + file = "Tests/images/pil136.tiff" + im = Image.open(file) - codecs = dir(Image.core) - if "jpeg_decoder" not in codecs: - skip("jpeg support not available") + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (55, 43)) + self.assertEqual(im.tile, [('raw', (0, 0, 55, 43), 8, ('RGBa', 0, 1))]) + im.load() - file = "Tests/images/pil168.tif" - im = Image.open(file) + def test_gimp_tiff(self): + # Read TIFF JPEG images from GIMP [@PIL168] - assert_equal(im.mode, "RGB") - assert_equal(im.size, (256, 256)) - assert_equal(im.tile, [ - ('jpeg', (0, 0, 256, 64), 8, ('RGB', '')), - ('jpeg', (0, 64, 256, 128), 1215, ('RGB', '')), - ('jpeg', (0, 128, 256, 192), 2550, ('RGB', '')), - ('jpeg', (0, 192, 256, 256), 3890, ('RGB', '')), - ]) - assert_no_exception(lambda: im.load()) + codecs = dir(Image.core) + if "jpeg_decoder" not in codecs: + self.skipTest("jpeg support not available") -def test_xyres_tiff(): - from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION - file = "Tests/images/pil168.tif" - im = Image.open(file) - assert isinstance(im.tag.tags[X_RESOLUTION][0], tuple) - assert isinstance(im.tag.tags[Y_RESOLUTION][0], tuple) - #Try to read a file where X,Y_RESOLUTION are ints - im.tag.tags[X_RESOLUTION] = (72,) - im.tag.tags[Y_RESOLUTION] = (72,) - im._setup() - assert_equal(im.info['dpi'], (72., 72.)) + file = "Tests/images/pil168.tif" + im = Image.open(file) + + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (256, 256)) + self.assertEqual( + im.tile, [ + ('jpeg', (0, 0, 256, 64), 8, ('RGB', '')), + ('jpeg', (0, 64, 256, 128), 1215, ('RGB', '')), + ('jpeg', (0, 128, 256, 192), 2550, ('RGB', '')), + ('jpeg', (0, 192, 256, 256), 3890, ('RGB', '')), + ]) + im.load() + + def test_xyres_tiff(self): + from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION + file = "Tests/images/pil168.tif" + im = Image.open(file) + assert isinstance(im.tag.tags[X_RESOLUTION][0], tuple) + assert isinstance(im.tag.tags[Y_RESOLUTION][0], tuple) + # Try to read a file where X,Y_RESOLUTION are ints + im.tag.tags[X_RESOLUTION] = (72,) + im.tag.tags[Y_RESOLUTION] = (72,) + im._setup() + self.assertEqual(im.info['dpi'], (72., 72.)) + + def test_little_endian(self): + im = Image.open('Tests/images/16bit.cropped.tif') + self.assertEqual(im.getpixel((0, 0)), 480) + self.assertEqual(im.mode, 'I;16') + + b = im.tobytes() + # Bytes are in image native order (little endian) + if py3: + self.assertEqual(b[0], ord(b'\xe0')) + self.assertEqual(b[1], ord(b'\x01')) + else: + self.assertEqual(b[0], b'\xe0') + self.assertEqual(b[1], b'\x01') + + def test_big_endian(self): + im = Image.open('Tests/images/16bit.MM.cropped.tif') + self.assertEqual(im.getpixel((0, 0)), 480) + self.assertEqual(im.mode, 'I;16B') + + b = im.tobytes() + + # Bytes are in image native order (big endian) + if py3: + self.assertEqual(b[0], ord(b'\x01')) + self.assertEqual(b[1], ord(b'\xe0')) + else: + self.assertEqual(b[0], b'\x01') + self.assertEqual(b[1], b'\xe0') + + def test_12bit_rawmode(self): + """ Are we generating the same interpretation + of the image as Imagemagick is? """ + + # Image.DEBUG = True + im = Image.open('Tests/images/12bit.cropped.tif') + + # to make the target -- + # convert 12bit.cropped.tif -depth 16 tmp.tif + # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif + # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, + # so we need to unshift so that the integer values are the same. + + im2 = Image.open('Tests/images/12in16bit.tif') + + if Image.DEBUG: + print (im.getpixel((0, 0))) + print (im.getpixel((0, 1))) + print (im.getpixel((0, 2))) + + print (im2.getpixel((0, 0))) + print (im2.getpixel((0, 1))) + print (im2.getpixel((0, 2))) + + self.assert_image_equal(im, im2) + + def test_32bit_float(self): + # Issue 614, specific 32 bit float format + path = 'Tests/images/10ct_32bit_128.tiff' + im = Image.open(path) + im.load() + + self.assertEqual(im.getpixel((0, 0)), -0.4526388943195343) + self.assertEqual( + im.getextrema(), (-3.140936851501465, 3.140684127807617)) -def test_little_endian(): - im = Image.open('Tests/images/16bit.cropped.tif') - assert_equal(im.getpixel((0,0)), 480) - assert_equal(im.mode, 'I;16') +if __name__ == '__main__': + unittest.main() - b = im.tobytes() - # Bytes are in image native order (little endian) - if py3: - assert_equal(b[0], ord(b'\xe0')) - assert_equal(b[1], ord(b'\x01')) - else: - assert_equal(b[0], b'\xe0') - assert_equal(b[1], b'\x01') - - -def test_big_endian(): - im = Image.open('Tests/images/16bit.MM.cropped.tif') - assert_equal(im.getpixel((0,0)), 480) - assert_equal(im.mode, 'I;16B') - - b = im.tobytes() - - # Bytes are in image native order (big endian) - if py3: - assert_equal(b[0], ord(b'\x01')) - assert_equal(b[1], ord(b'\xe0')) - else: - assert_equal(b[0], b'\x01') - assert_equal(b[1], b'\xe0') - - -def test_12bit_rawmode(): - """ Are we generating the same interpretation of the image as Imagemagick is? """ - - #Image.DEBUG = True - im = Image.open('Tests/images/12bit.cropped.tif') - - # to make the target -- - # convert 12bit.cropped.tif -depth 16 tmp.tif - # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif - # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, - # so we need to unshift so that the integer values are the same. - - im2 = Image.open('Tests/images/12in16bit.tif') - - if Image.DEBUG: - print (im.getpixel((0,0))) - print (im.getpixel((0,1))) - print (im.getpixel((0,2))) - - print (im2.getpixel((0,0))) - print (im2.getpixel((0,1))) - print (im2.getpixel((0,2))) - - assert_image_equal(im, im2) - -def test_32bit_float(): - # Issue 614, specific 32 bit float format - path = 'Tests/images/10ct_32bit_128.tiff' - im = Image.open(path) - im.load() - - assert_equal(im.getpixel((0,0)), -0.4526388943195343) - assert_equal(im.getextrema(), (-3.140936851501465, 3.140684127807617)) - - +# End of file diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index c759d8c0d..2019f3455 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -1,80 +1,93 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena + from PIL import Image, TiffImagePlugin, TiffTags tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys())) -def test_rt_metadata(): - """ Test writing arbitray metadata into the tiff image directory - Use case is ImageJ private tags, one numeric, one arbitrary - data. https://github.com/python-pillow/Pillow/issues/291 - """ - img = lena() +class TestFileTiffMetadata(PillowTestCase): - textdata = "This is some arbitrary metadata for a text field" - info = TiffImagePlugin.ImageFileDirectory() + def test_rt_metadata(self): + """ Test writing arbitray metadata into the tiff image directory + Use case is ImageJ private tags, one numeric, one arbitrary + data. https://github.com/python-pillow/Pillow/issues/291 + """ - info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata) - info[tag_ids['ImageJMetaData']] = textdata + img = lena() - f = tempfile("temp.tif") + textdata = "This is some arbitrary metadata for a text field" + info = TiffImagePlugin.ImageFileDirectory() - img.save(f, tiffinfo=info) + info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata) + info[tag_ids['ImageJMetaData']] = textdata - loaded = Image.open(f) + f = self.tempfile("temp.tif") - assert_equal(loaded.tag[50838], (len(textdata),)) - assert_equal(loaded.tag[50839], textdata) + img.save(f, tiffinfo=info) -def test_read_metadata(): - img = Image.open('Tests/images/lena_g4.tif') + loaded = Image.open(f) - known = {'YResolution': ((1207959552, 16777216),), - 'PlanarConfiguration': (1,), - 'BitsPerSample': (1,), - 'ImageLength': (128,), - 'Compression': (4,), - 'FillOrder': (1,), - 'DocumentName': 'lena.g4.tif', - 'RowsPerStrip': (128,), - 'ResolutionUnit': (1,), - 'PhotometricInterpretation': (0,), - 'PageNumber': (0, 1), - 'XResolution': ((1207959552, 16777216),), - 'ImageWidth': (128,), - 'Orientation': (1,), - 'StripByteCounts': (1796,), - 'SamplesPerPixel': (1,), - 'StripOffsets': (8,), - 'Software': 'ImageMagick 6.5.7-8 2012-08-17 Q16 http://www.imagemagick.org'} + self.assertEqual(loaded.tag[50838], (len(textdata),)) + self.assertEqual(loaded.tag[50839], textdata) - # assert_equal is equivalent, but less helpful in telling what's wrong. - named = img.tag.named() - for tag, value in named.items(): - assert_equal(known[tag], value) + def test_read_metadata(self): + img = Image.open('Tests/images/lena_g4.tif') - for tag, value in known.items(): - assert_equal(value, named[tag]) + known = {'YResolution': ((1207959552, 16777216),), + 'PlanarConfiguration': (1,), + 'BitsPerSample': (1,), + 'ImageLength': (128,), + 'Compression': (4,), + 'FillOrder': (1,), + 'DocumentName': 'lena.g4.tif', + 'RowsPerStrip': (128,), + 'ResolutionUnit': (1,), + 'PhotometricInterpretation': (0,), + 'PageNumber': (0, 1), + 'XResolution': ((1207959552, 16777216),), + 'ImageWidth': (128,), + 'Orientation': (1,), + 'StripByteCounts': (1796,), + 'SamplesPerPixel': (1,), + 'StripOffsets': (8,), + 'Software': 'ImageMagick 6.5.7-8 2012-08-17 Q16 http://www.imagemagick.org'} + + # self.assertEqual is equivalent, + # but less helpful in telling what's wrong. + named = img.tag.named() + for tag, value in named.items(): + self.assertEqual(known[tag], value) + + for tag, value in known.items(): + self.assertEqual(value, named[tag]) + + def test_write_metadata(self): + """ Test metadata writing through the python code """ + img = Image.open('Tests/images/lena.tif') + + f = self.tempfile('temp.tiff') + img.save(f, tiffinfo=img.tag) + + loaded = Image.open(f) + + original = img.tag.named() + reloaded = loaded.tag.named() + + ignored = [ + 'StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets'] + + for tag, value in reloaded.items(): + if tag not in ignored: + self.assertEqual( + original[tag], value, "%s didn't roundtrip" % tag) + + for tag, value in original.items(): + if tag not in ignored: + self.assertEqual( + value, reloaded[tag], "%s didn't roundtrip" % tag) -def test_write_metadata(): - """ Test metadata writing through the python code """ - img = Image.open('Tests/images/lena.tif') +if __name__ == '__main__': + unittest.main() - f = tempfile('temp.tiff') - img.save(f, tiffinfo = img.tag) - - loaded = Image.open(f) - - original = img.tag.named() - reloaded = loaded.tag.named() - - ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets'] - - for tag, value in reloaded.items(): - if tag not in ignored: - assert_equal(original[tag], value, "%s didn't roundtrip" % tag) - - for tag, value in original.items(): - if tag not in ignored: - assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag) +# End of file diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index a89dea3c4..ea1c2e19f 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -1,68 +1,79 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image - try: from PIL import _webp except: - skip('webp support not installed') + # Skip in setUp() + pass -def test_version(): - assert_no_exception(lambda: _webp.WebPDecoderVersion()) - assert_no_exception(lambda: _webp.WebPDecoderBuggyAlpha()) +class TestFileWebp(PillowTestCase): -def test_read_rgb(): + def setUp(self): + try: + from PIL import _webp + except: + self.skipTest('WebP support not installed') - file_path = "Images/lena.webp" - image = Image.open(file_path) + def test_version(self): + _webp.WebPDecoderVersion() + _webp.WebPDecoderBuggyAlpha() - assert_equal(image.mode, "RGB") - assert_equal(image.size, (128, 128)) - assert_equal(image.format, "WEBP") - assert_no_exception(lambda: image.load()) - assert_no_exception(lambda: image.getdata()) - - # generated with: dwebp -ppm ../../Images/lena.webp -o lena_webp_bits.ppm - target = Image.open('Tests/images/lena_webp_bits.ppm') - assert_image_similar(image, target, 20.0) - - -def test_write_rgb(): - """ - Can we write a RGB mode file to webp without error. Does it have the bits we - expect? - - """ - - temp_file = tempfile("temp.webp") - - lena("RGB").save(temp_file) - - image = Image.open(temp_file) - image.load() - - assert_equal(image.mode, "RGB") - assert_equal(image.size, (128, 128)) - assert_equal(image.format, "WEBP") - assert_no_exception(lambda: image.load()) - assert_no_exception(lambda: image.getdata()) - - # If we're using the exact same version of webp, this test should pass. - # but it doesn't if the webp is generated on Ubuntu and tested on Fedora. - - # generated with: dwebp -ppm temp.webp -o lena_webp_write.ppm - #target = Image.open('Tests/images/lena_webp_write.ppm') - #assert_image_equal(image, target) - - # This test asserts that the images are similar. If the average pixel difference - # between the two images is less than the epsilon value, then we're going to - # accept that it's a reasonable lossy version of the image. The included lena images - # for webp are showing ~16 on Ubuntu, the jpegs are showing ~18. - target = lena("RGB") - assert_image_similar(image, target, 20.0) + def test_read_rgb(self): + file_path = "Images/lena.webp" + image = Image.open(file_path) + + self.assertEqual(image.mode, "RGB") + self.assertEqual(image.size, (128, 128)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() + + # generated with: + # dwebp -ppm ../../Images/lena.webp -o lena_webp_bits.ppm + target = Image.open('Tests/images/lena_webp_bits.ppm') + self.assert_image_similar(image, target, 20.0) + + def test_write_rgb(self): + """ + Can we write a RGB mode file to webp without error. + Does it have the bits we expect? + """ + + temp_file = self.tempfile("temp.webp") + + lena("RGB").save(temp_file) + + image = Image.open(temp_file) + image.load() + + self.assertEqual(image.mode, "RGB") + self.assertEqual(image.size, (128, 128)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() + + # If we're using the exact same version of WebP, this test should pass. + # but it doesn't if the WebP is generated on Ubuntu and tested on + # Fedora. + + # generated with: dwebp -ppm temp.webp -o lena_webp_write.ppm + # target = Image.open('Tests/images/lena_webp_write.ppm') + # self.assert_image_equal(image, target) + + # This test asserts that the images are similar. If the average pixel + # difference between the two images is less than the epsilon value, + # then we're going to accept that it's a reasonable lossy version of + # the image. The included lena images for WebP are showing ~16 on + # Ubuntu, the jpegs are showing ~18. + target = lena("RGB") + self.assert_image_similar(image, target, 20.0) +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index 5ac03b9d4..c9787c60e 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -1,82 +1,91 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image try: from PIL import _webp except: - skip('webp support not installed') + pass + # Skip in setUp() -if _webp.WebPDecoderBuggyAlpha(): - skip("Buggy early version of webp installed, not testing transparency") +class TestFileWebpAlpha(PillowTestCase): -def test_read_rgba(): - # Generated with `cwebp transparent.png -o transparent.webp` - file_path = "Images/transparent.webp" - image = Image.open(file_path) + def setUp(self): + try: + from PIL import _webp + except: + self.skipTest('WebP support not installed') - assert_equal(image.mode, "RGBA") - assert_equal(image.size, (200, 150)) - assert_equal(image.format, "WEBP") - assert_no_exception(lambda: image.load()) - assert_no_exception(lambda: image.getdata()) + if _webp.WebPDecoderBuggyAlpha(self): + self.skipTest("Buggy early version of WebP installed, not testing transparency") - orig_bytes = image.tobytes() - - target = Image.open('Images/transparent.png') - assert_image_similar(image, target, 20.0) - - -def test_write_lossless_rgb(): - temp_file = tempfile("temp.webp") - #temp_file = "temp.webp" - - pil_image = lena('RGBA') - - mask = Image.new("RGBA", (64, 64), (128,128,128,128)) - pil_image.paste(mask, (0,0), mask) # add some partially transparent bits. - - pil_image.save(temp_file, lossless=True) - - image = Image.open(temp_file) - image.load() - - assert_equal(image.mode, "RGBA") - assert_equal(image.size, pil_image.size) - assert_equal(image.format, "WEBP") - assert_no_exception(lambda: image.load()) - assert_no_exception(lambda: image.getdata()) - - - assert_image_equal(image, pil_image) - -def test_write_rgba(): - """ - Can we write a RGBA mode file to webp without error. Does it have the bits we - expect? - - """ - - temp_file = tempfile("temp.webp") - - pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20)) - pil_image.save(temp_file) - - if _webp.WebPDecoderBuggyAlpha(): - return - - image = Image.open(temp_file) - image.load() - - assert_equal(image.mode, "RGBA") - assert_equal(image.size, (10, 10)) - assert_equal(image.format, "WEBP") - assert_no_exception(image.load) - assert_no_exception(image.getdata) - - assert_image_similar(image, pil_image, 1.0) + def test_read_rgba(self): + # Generated with `cwebp transparent.png -o transparent.webp` + file_path = "Images/transparent.webp" + image = Image.open(file_path) + self.assertEqual(image.mode, "RGBA") + self.assertEqual(image.size, (200, 150)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() + + image.tobytes() + + target = Image.open('Images/transparent.png') + self.assert_image_similar(image, target, 20.0) + + def test_write_lossless_rgb(self): + temp_file = self.tempfile("temp.webp") + # temp_file = "temp.webp" + + pil_image = lena('RGBA') + + mask = Image.new("RGBA", (64, 64), (128, 128, 128, 128)) + # Add some partially transparent bits: + pil_image.paste(mask, (0, 0), mask) + + pil_image.save(temp_file, lossless=True) + + image = Image.open(temp_file) + image.load() + + self.assertEqual(image.mode, "RGBA") + self.assertEqual(image.size, pil_image.size) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() + + self.assert_image_equal(image, pil_image) + + def test_write_rgba(self): + """ + Can we write a RGBA mode file to webp without error. + Does it have the bits we expect? + """ + + temp_file = self.tempfile("temp.webp") + + pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20)) + pil_image.save(temp_file) + + if _webp.WebPDecoderBuggyAlpha(self): + return + + image = Image.open(temp_file) + image.load() + + self.assertEqual(image.mode, "RGBA") + self.assertEqual(image.size, (10, 10)) + self.assertEqual(image.format, "WEBP") + image.load + image.getdata + + self.assert_image_similar(image, pil_image, 1.0) +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_file_webp_lossless.py b/Tests/test_file_webp_lossless.py index ca2b5af19..9f8e339de 100644 --- a/Tests/test_file_webp_lossless.py +++ b/Tests/test_file_webp_lossless.py @@ -1,33 +1,43 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image - try: from PIL import _webp except: - skip('webp support not installed') + pass + # Skip in setUp() -if (_webp.WebPDecoderVersion() < 0x0200): - skip('lossless not included') +class TestFileWebpLossless(PillowTestCase): -def test_write_lossless_rgb(): - temp_file = tempfile("temp.webp") + def setUp(self): + try: + from PIL import _webp + except: + self.skipTest('WebP support not installed') - lena("RGB").save(temp_file, lossless=True) + if (_webp.WebPDecoderVersion() < 0x0200): + self.skipTest('lossless not included') - image = Image.open(temp_file) - image.load() + def test_write_lossless_rgb(self): + temp_file = self.tempfile("temp.webp") - assert_equal(image.mode, "RGB") - assert_equal(image.size, (128, 128)) - assert_equal(image.format, "WEBP") - assert_no_exception(lambda: image.load()) - assert_no_exception(lambda: image.getdata()) - - - assert_image_equal(image, lena("RGB")) + lena("RGB").save(temp_file, lossless=True) + + image = Image.open(temp_file) + image.load() + + self.assertEqual(image.mode, "RGB") + self.assertEqual(image.size, (128, 128)) + self.assertEqual(image.format, "WEBP") + image.load() + image.getdata() + + self.assert_image_equal(image, lena("RGB")) +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index b4146c3ee..93021ac07 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -1,101 +1,112 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image -try: - from PIL import _webp - if not _webp.HAVE_WEBPMUX: - skip('webpmux support not installed') -except: - skip('webp support not installed') + +class TestFileWebpMetadata(PillowTestCase): + + def setUp(self): + try: + from PIL import _webp + if not _webp.HAVE_WEBPMUX: + self.skipTest('webpmux support not installed') + except: + self.skipTest('WebP support not installed') + + def test_read_exif_metadata(self): + + file_path = "Images/flower.webp" + image = Image.open(file_path) + + self.assertEqual(image.format, "WEBP") + exif_data = image.info.get("exif", None) + self.assertTrue(exif_data) + + exif = image._getexif() + + # camera make + self.assertEqual(exif[271], "Canon") + + jpeg_image = Image.open('Tests/images/flower.jpg') + expected_exif = jpeg_image.info['exif'] + + self.assertEqual(exif_data, expected_exif) + + def test_write_exif_metadata(self): + from io import BytesIO + + file_path = "Tests/images/flower.jpg" + image = Image.open(file_path) + expected_exif = image.info['exif'] + + buffer = BytesIO() + + image.save(buffer, "webp", exif=expected_exif) + + buffer.seek(0) + webp_image = Image.open(buffer) + + webp_exif = webp_image.info.get('exif', None) + self.assertTrue(webp_exif) + if webp_exif: + self.assertEqual( + webp_exif, expected_exif, "WebP EXIF didn't match") + + def test_read_icc_profile(self): + + file_path = "Images/flower2.webp" + image = Image.open(file_path) + + self.assertEqual(image.format, "WEBP") + self.assertTrue(image.info.get("icc_profile", None)) + + icc = image.info['icc_profile'] + + jpeg_image = Image.open('Tests/images/flower2.jpg') + expected_icc = jpeg_image.info['icc_profile'] + + self.assertEqual(icc, expected_icc) + + def test_write_icc_metadata(self): + from io import BytesIO + + file_path = "Tests/images/flower2.jpg" + image = Image.open(file_path) + expected_icc_profile = image.info['icc_profile'] + + buffer = BytesIO() + + image.save(buffer, "webp", icc_profile=expected_icc_profile) + + buffer.seek(0) + webp_image = Image.open(buffer) + + webp_icc_profile = webp_image.info.get('icc_profile', None) + + self.assertTrue(webp_icc_profile) + if webp_icc_profile: + self.assertEqual( + webp_icc_profile, expected_icc_profile, + "Webp ICC didn't match") + + def test_read_no_exif(self): + from io import BytesIO + + file_path = "Tests/images/flower.jpg" + image = Image.open(file_path) + image.info['exif'] + + buffer = BytesIO() + + image.save(buffer, "webp") + + buffer.seek(0) + webp_image = Image.open(buffer) + + self.assertFalse(webp_image._getexif()) +if __name__ == '__main__': + unittest.main() -def test_read_exif_metadata(): - - file_path = "Images/flower.webp" - image = Image.open(file_path) - - assert_equal(image.format, "WEBP") - exif_data = image.info.get("exif", None) - assert_true(exif_data) - - exif = image._getexif() - - #camera make - assert_equal(exif[271], "Canon") - - jpeg_image = Image.open('Tests/images/flower.jpg') - expected_exif = jpeg_image.info['exif'] - - assert_equal(exif_data, expected_exif) - - -def test_write_exif_metadata(): - file_path = "Tests/images/flower.jpg" - image = Image.open(file_path) - expected_exif = image.info['exif'] - - buffer = BytesIO() - - image.save(buffer, "webp", exif=expected_exif) - - buffer.seek(0) - webp_image = Image.open(buffer) - - webp_exif = webp_image.info.get('exif', None) - assert_true(webp_exif) - if webp_exif: - assert_equal(webp_exif, expected_exif, "Webp Exif didn't match") - - -def test_read_icc_profile(): - - file_path = "Images/flower2.webp" - image = Image.open(file_path) - - assert_equal(image.format, "WEBP") - assert_true(image.info.get("icc_profile", None)) - - icc = image.info['icc_profile'] - - jpeg_image = Image.open('Tests/images/flower2.jpg') - expected_icc = jpeg_image.info['icc_profile'] - - assert_equal(icc, expected_icc) - - -def test_write_icc_metadata(): - file_path = "Tests/images/flower2.jpg" - image = Image.open(file_path) - expected_icc_profile = image.info['icc_profile'] - - buffer = BytesIO() - - image.save(buffer, "webp", icc_profile=expected_icc_profile) - - buffer.seek(0) - webp_image = Image.open(buffer) - - webp_icc_profile = webp_image.info.get('icc_profile', None) - - assert_true(webp_icc_profile) - if webp_icc_profile: - assert_equal(webp_icc_profile, expected_icc_profile, "Webp ICC didn't match") - - -def test_read_no_exif(): - file_path = "Tests/images/flower.jpg" - image = Image.open(file_path) - expected_exif = image.info['exif'] - - buffer = BytesIO() - - image.save(buffer, "webp") - - buffer.seek(0) - webp_image = Image.open(buffer) - - assert_false(webp_image._getexif()) - - +# End of file diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py index f27a3a349..d520ef460 100644 --- a/Tests/test_file_xbm.py +++ b/Tests/test_file_xbm.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image @@ -25,10 +25,20 @@ static char basic_bits[] = { }; """ -def test_pil151(): - im = Image.open(BytesIO(PIL151)) +class TestFileXbm(PillowTestCase): - assert_no_exception(lambda: im.load()) - assert_equal(im.mode, '1') - assert_equal(im.size, (32, 32)) + def test_pil151(self): + from io import BytesIO + + im = Image.open(BytesIO(PIL151)) + + im.load() + self.assertEqual(im.mode, '1') + self.assertEqual(im.size, (32, 32)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py index 44135d028..0c765da8e 100644 --- a/Tests/test_file_xpm.py +++ b/Tests/test_file_xpm.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image @@ -6,9 +6,18 @@ from PIL import Image file = "Images/lena.xpm" data = open(file, "rb").read() -def test_sanity(): - im = Image.open(file) - im.load() - assert_equal(im.mode, "P") - assert_equal(im.size, (128, 128)) - assert_equal(im.format, "XPM") + +class TestFileXpm(PillowTestCase): + + def test_sanity(self): + im = Image.open(file) + im.load() + self.assertEqual(im.mode, "P") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "XPM") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py index 366bb4468..9a8f19d03 100644 --- a/Tests/test_font_bdf.py +++ b/Tests/test_font_bdf.py @@ -1,13 +1,22 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule -from PIL import Image, FontFile, BdfFontFile +from PIL import FontFile, BdfFontFile filename = "Images/courB08.bdf" -def test_sanity(): - file = open(filename, "rb") - font = BdfFontFile.BdfFontFile(file) +class TestFontBdf(PillowTestCase): - assert_true(isinstance(font, FontFile.FontFile)) - assert_equal(len([_f for _f in font.glyph if _f]), 190) + def test_sanity(self): + + file = open(filename, "rb") + font = BdfFontFile.BdfFontFile(file) + + self.assertIsInstance(font, FontFile.FontFile) + self.assertEqual(len([_f for _f in font.glyph if _f]), 190) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index bae214e35..24abcf73d 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -1,49 +1,63 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image, FontFile, PcfFontFile from PIL import ImageFont, ImageDraw codecs = dir(Image.core) -if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - skip("zlib support not available") - fontname = "Tests/fonts/helvO18.pcf" -tempname = tempfile("temp.pil", "temp.pbm") -message = "hello, world" +message = "hello, world" -def test_sanity(): - file = open(fontname, "rb") - font = PcfFontFile.PcfFontFile(file) - assert_true(isinstance(font, FontFile.FontFile)) - assert_equal(len([_f for _f in font.glyph if _f]), 192) +class TestFontPcf(PillowTestCase): - font.save(tempname) + def setUp(self): + if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + self.skipTest("zlib support not available") -def xtest_draw(): + def save_font(self): + file = open(fontname, "rb") + font = PcfFontFile.PcfFontFile(file) + self.assertIsInstance(font, FontFile.FontFile) + self.assertEqual(len([_f for _f in font.glyph if _f]), 192) - font = ImageFont.load(tempname) - image = Image.new("L", font.getsize(message), "white") - draw = ImageDraw.Draw(image) - draw.text((0, 0), message, font=font) - # assert_signature(image, "7216c60f988dea43a46bb68321e3c1b03ec62aee") + tempname = self.tempfile("temp.pil", "temp.pbm") + font.save(tempname) + return tempname -def _test_high_characters(message): + def test_sanity(self): + self.save_font() - font = ImageFont.load(tempname) - image = Image.new("L", font.getsize(message), "white") - draw = ImageDraw.Draw(image) - draw.text((0, 0), message, font=font) + def xtest_draw(self): - compare = Image.open('Tests/images/high_ascii_chars.png') - assert_image_equal(image, compare) + tempname = self.save_font() + font = ImageFont.load(tempname) + image = Image.new("L", font.getsize(message), "white") + draw = ImageDraw.Draw(image) + draw.text((0, 0), message, font=font) + # assert_signature(image, "7216c60f988dea43a46bb68321e3c1b03ec62aee") -def test_high_characters(): - message = "".join([chr(i+1) for i in range(140,232)]) - _test_high_characters(message) - # accept bytes instances in Py3. - if bytes is not str: - _test_high_characters(message.encode('latin1')) + def _test_high_characters(self, message): + tempname = self.save_font() + font = ImageFont.load(tempname) + image = Image.new("L", font.getsize(message), "white") + draw = ImageDraw.Draw(image) + draw.text((0, 0), message, font=font) + + compare = Image.open('Tests/images/high_ascii_chars.png') + self.assert_image_equal(image, compare) + + def test_high_characters(self): + message = "".join([chr(i+1) for i in range(140, 232)]) + self._test_high_characters(message) + # accept bytes instances in Py3. + if bytes is not str: + self._test_high_characters(message.encode('latin1')) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py index 371b06a0b..188b0d1fa 100644 --- a/Tests/test_format_lab.py +++ b/Tests/test_format_lab.py @@ -1,41 +1,48 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image -def test_white(): - i = Image.open('Tests/images/lab.tif') - bits = i.load() - - assert_equal(i.mode, 'LAB') +class TestFormatLab(PillowTestCase): - assert_equal(i.getbands(), ('L','A', 'B')) + def test_white(self): + i = Image.open('Tests/images/lab.tif') - k = i.getpixel((0,0)) - assert_equal(k, (255,128,128)) + i.load() - L = i.getdata(0) - a = i.getdata(1) - b = i.getdata(2) + self.assertEqual(i.mode, 'LAB') - assert_equal(list(L), [255]*100) - assert_equal(list(a), [128]*100) - assert_equal(list(b), [128]*100) - + self.assertEqual(i.getbands(), ('L', 'A', 'B')) -def test_green(): - # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS - # == RGB: 0, 152, 117 - i = Image.open('Tests/images/lab-green.tif') + k = i.getpixel((0, 0)) + self.assertEqual(k, (255, 128, 128)) - k = i.getpixel((0,0)) - assert_equal(k, (128,28,128)) + L = i.getdata(0) + a = i.getdata(1) + b = i.getdata(2) + + self.assertEqual(list(L), [255]*100) + self.assertEqual(list(a), [128]*100) + self.assertEqual(list(b), [128]*100) + + def test_green(self): + # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS + # == RGB: 0, 152, 117 + i = Image.open('Tests/images/lab-green.tif') + + k = i.getpixel((0, 0)) + self.assertEqual(k, (128, 28, 128)) + + def test_red(self): + # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS + # == RGB: 255, 0, 124 + i = Image.open('Tests/images/lab-red.tif') + + k = i.getpixel((0, 0)) + self.assertEqual(k, (128, 228, 128)) -def test_red(): - # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS - # == RGB: 255, 0, 124 - i = Image.open('Tests/images/lab-red.tif') +if __name__ == '__main__': + unittest.main() - k = i.getpixel((0,0)) - assert_equal(k, (128,228,128)) +# End of file diff --git a/Tests/test_image.py b/Tests/test_image.py index 26c699e66..ec82eb419 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1,39 +1,51 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image -def test_sanity(): - im = Image.new("L", (100, 100)) - assert_equal(repr(im)[:45], " 8 bit lut for converting I->L images - see https://github.com/python-pillow/Pillow/issues/440 - """ +class TestImagePoint(PillowTestCase): - im = lena("I") - assert_no_exception(lambda: im.point(list(range(256))*256, 'L')) + def setUp(self): + if hasattr(sys, 'pypy_version_info'): + # This takes _forever_ on PyPy. Open Bug, + # see https://github.com/python-pillow/Pillow/issues/484 + self.skipTest("Too slow on PyPy") + + def test_sanity(self): + im = lena() + + self.assertRaises(ValueError, lambda: im.point(list(range(256)))) + im.point(list(range(256))*3) + im.point(lambda x: x) + + im = im.convert("I") + self.assertRaises(ValueError, lambda: im.point(list(range(256)))) + im.point(lambda x: x*1) + im.point(lambda x: x+1) + im.point(lambda x: x*1+1) + self.assertRaises(TypeError, lambda: im.point(lambda x: x-1)) + self.assertRaises(TypeError, lambda: im.point(lambda x: x/1)) + + def test_16bit_lut(self): + """ Tests for 16 bit -> 8 bit lut for converting I->L images + see https://github.com/python-pillow/Pillow/issues/440 + """ + + im = lena("I") + im.point(list(range(256))*256, 'L') + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_putalpha.py b/Tests/test_image_putalpha.py index b23f69834..bb36b335e 100644 --- a/Tests/test_image_putalpha.py +++ b/Tests/test_image_putalpha.py @@ -1,43 +1,52 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image -def test_interface(): - im = Image.new("RGBA", (1, 1), (1, 2, 3, 0)) - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 0)) +class TestImagePutAlpha(PillowTestCase): - im = Image.new("RGBA", (1, 1), (1, 2, 3)) - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 255)) + def test_interface(self): - im.putalpha(Image.new("L", im.size, 4)) - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) + im = Image.new("RGBA", (1, 1), (1, 2, 3, 0)) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 0)) - im.putalpha(5) - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 5)) + im = Image.new("RGBA", (1, 1), (1, 2, 3)) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 255)) -def test_promote(): + im.putalpha(Image.new("L", im.size, 4)) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) - im = Image.new("L", (1, 1), 1) - assert_equal(im.getpixel((0, 0)), 1) + im.putalpha(5) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 5)) - im.putalpha(2) - assert_equal(im.mode, 'LA') - assert_equal(im.getpixel((0, 0)), (1, 2)) + def test_promote(self): - im = Image.new("RGB", (1, 1), (1, 2, 3)) - assert_equal(im.getpixel((0, 0)), (1, 2, 3)) + im = Image.new("L", (1, 1), 1) + self.assertEqual(im.getpixel((0, 0)), 1) - im.putalpha(4) - assert_equal(im.mode, 'RGBA') - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) + im.putalpha(2) + self.assertEqual(im.mode, 'LA') + self.assertEqual(im.getpixel((0, 0)), (1, 2)) -def test_readonly(): + im = Image.new("RGB", (1, 1), (1, 2, 3)) + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3)) - im = Image.new("RGB", (1, 1), (1, 2, 3)) - im.readonly = 1 + im.putalpha(4) + self.assertEqual(im.mode, 'RGBA') + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) - im.putalpha(4) - assert_false(im.readonly) - assert_equal(im.mode, 'RGBA') - assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) + def test_readonly(self): + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.readonly = 1 + + im.putalpha(4) + self.assertFalse(im.readonly) + self.assertEqual(im.mode, 'RGBA') + self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index e25359fdf..d792adfe6 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -1,40 +1,48 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena import sys from PIL import Image -def test_sanity(): - im1 = lena() +class TestImagePutData(PillowTestCase): - data = list(im1.getdata()) + def test_sanity(self): - im2 = Image.new(im1.mode, im1.size, 0) - im2.putdata(data) + im1 = lena() - assert_image_equal(im1, im2) + data = list(im1.getdata()) - # readonly - im2 = Image.new(im1.mode, im2.size, 0) - im2.readonly = 1 - im2.putdata(data) + im2 = Image.new(im1.mode, im1.size, 0) + im2.putdata(data) - assert_false(im2.readonly) - assert_image_equal(im1, im2) + self.assert_image_equal(im1, im2) + + # readonly + im2 = Image.new(im1.mode, im2.size, 0) + im2.readonly = 1 + im2.putdata(data) + + self.assertFalse(im2.readonly) + self.assert_image_equal(im1, im2) + + def test_long_integers(self): + # see bug-200802-systemerror + def put(value): + im = Image.new("RGBA", (1, 1)) + im.putdata([value]) + return im.getpixel((0, 0)) + self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) + self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) + self.assertEqual(put(-1), (255, 255, 255, 255)) + self.assertEqual(put(-1), (255, 255, 255, 255)) + if sys.maxsize > 2**32: + self.assertEqual(put(sys.maxsize), (255, 255, 255, 255)) + else: + self.assertEqual(put(sys.maxsize), (255, 255, 255, 127)) -def test_long_integers(): - # see bug-200802-systemerror - def put(value): - im = Image.new("RGBA", (1, 1)) - im.putdata([value]) - return im.getpixel((0, 0)) - assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255)) - assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255)) - assert_equal(put(-1), (255, 255, 255, 255)) - assert_equal(put(-1), (255, 255, 255, 255)) - if sys.maxsize > 2**32: - assert_equal(put(sys.maxsize), (255, 255, 255, 255)) - else: - assert_equal(put(sys.maxsize), (255, 255, 255, 127)) +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index b7ebb8853..26ad09800 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -1,28 +1,36 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena -from PIL import Image from PIL import ImagePalette -def test_putpalette(): - def palette(mode): - im = lena(mode).copy() - im.putpalette(list(range(256))*3) - p = im.getpalette() - if p: - return im.mode, p[:10] - return im.mode - assert_exception(ValueError, lambda: palette("1")) - assert_equal(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) - assert_equal(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) - assert_exception(ValueError, lambda: palette("I")) - assert_exception(ValueError, lambda: palette("F")) - assert_exception(ValueError, lambda: palette("RGB")) - assert_exception(ValueError, lambda: palette("RGBA")) - assert_exception(ValueError, lambda: palette("YCbCr")) -def test_imagepalette(): - im = lena("P") - assert_no_exception(lambda: im.putpalette(ImagePalette.negative())) - assert_no_exception(lambda: im.putpalette(ImagePalette.random())) - assert_no_exception(lambda: im.putpalette(ImagePalette.sepia())) - assert_no_exception(lambda: im.putpalette(ImagePalette.wedge())) +class TestImagePutPalette(PillowTestCase): + + def test_putpalette(self): + def palette(mode): + im = lena(mode).copy() + im.putpalette(list(range(256))*3) + p = im.getpalette() + if p: + return im.mode, p[:10] + return im.mode + self.assertRaises(ValueError, lambda: palette("1")) + self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + self.assertRaises(ValueError, lambda: palette("I")) + self.assertRaises(ValueError, lambda: palette("F")) + self.assertRaises(ValueError, lambda: palette("RGB")) + self.assertRaises(ValueError, lambda: palette("RGBA")) + self.assertRaises(ValueError, lambda: palette("YCbCr")) + + def test_imagepalette(self): + im = lena("P") + im.putpalette(ImagePalette.negative()) + im.putpalette(ImagePalette.random()) + im.putpalette(ImagePalette.sepia()) + im.putpalette(ImagePalette.wedge()) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_putpixel.py b/Tests/test_image_putpixel.py index 5f19237cb..1afc013c0 100644 --- a/Tests/test_image_putpixel.py +++ b/Tests/test_image_putpixel.py @@ -1,45 +1,50 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image -Image.USE_CFFI_ACCESS=False - -def test_sanity(): - - im1 = lena() - im2 = Image.new(im1.mode, im1.size, 0) - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) - - assert_image_equal(im1, im2) - - im2 = Image.new(im1.mode, im1.size, 0) - im2.readonly = 1 - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pos = x, y - im2.putpixel(pos, im1.getpixel(pos)) - - assert_false(im2.readonly) - assert_image_equal(im1, im2) - - im2 = Image.new(im1.mode, im1.size, 0) - - pix1 = im1.load() - pix2 = im2.load() - - for y in range(im1.size[1]): - for x in range(im1.size[0]): - pix2[x, y] = pix1[x, y] - - assert_image_equal(im1, im2) +Image.USE_CFFI_ACCESS = False +class TestImagePutPixel(PillowTestCase): + + def test_sanity(self): + + im1 = lena() + im2 = Image.new(im1.mode, im1.size, 0) + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + self.assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.readonly = 1 + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + self.assertFalse(im2.readonly) + self.assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + + pix1 = im1.load() + pix2 = im2.load() + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pix2[x, y] = pix1[x, y] + + self.assert_image_equal(im1, im2) + + # see test_image_getpixel for more tests -# see test_image_getpixel for more tests +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index dbf68a25e..63fe0cb21 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -1,27 +1,35 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image -def test_sanity(): - im = lena() +class TestImageQuantize(PillowTestCase): - im = im.quantize() - assert_image(im, "P", im.size) + def test_sanity(self): + im = lena() - im = lena() - im = im.quantize(palette=lena("P")) - assert_image(im, "P", im.size) + im = im.quantize() + self.assert_image(im, "P", im.size) -def test_octree_quantize(): - im = lena() + im = lena() + im = im.quantize(palette=lena("P")) + self.assert_image(im, "P", im.size) - im = im.quantize(100, Image.FASTOCTREE) - assert_image(im, "P", im.size) + def test_octree_quantize(self): + im = lena() - assert len(im.getcolors()) == 100 + im = im.quantize(100, Image.FASTOCTREE) + self.assert_image(im, "P", im.size) -def test_rgba_quantize(): - im = lena('RGBA') - assert_no_exception(lambda: im.quantize()) - assert_exception(Exception, lambda: im.quantize(method=0)) + assert len(im.getcolors()) == 100 + + def test_rgba_quantize(self): + im = lena('RGBA') + im.quantize() + self.assertRaises(Exception, lambda: im.quantize(method=0)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 4e228a396..a200b17b4 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -1,12 +1,19 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena -from PIL import Image -def test_resize(): - def resize(mode, size): - out = lena(mode).resize(size) - assert_equal(out.mode, mode) - assert_equal(out.size, size) - for mode in "1", "P", "L", "RGB", "I", "F": - yield_test(resize, mode, (100, 100)) - yield_test(resize, mode, (200, 200)) +class TestImageResize(PillowTestCase): + + def test_resize(self): + def resize(mode, size): + out = lena(mode).resize(size) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, size) + for mode in "1", "P", "L", "RGB", "I", "F": + resize(mode, (100, 100)) + resize(mode, (200, 200)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 5e4782c87..bb24ddf4f 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -1,15 +1,22 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena -from PIL import Image -def test_rotate(): - def rotate(mode): - im = lena(mode) - out = im.rotate(45) - assert_equal(out.mode, mode) - assert_equal(out.size, im.size) # default rotate clips output - out = im.rotate(45, expand=1) - assert_equal(out.mode, mode) - assert_true(out.size != im.size) - for mode in "1", "P", "L", "RGB", "I", "F": - yield_test(rotate, mode) +class TestImageRotate(PillowTestCase): + + def test_rotate(self): + def rotate(mode): + im = lena(mode) + out = im.rotate(45) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) # default rotate clips output + out = im.rotate(45, expand=1) + self.assertEqual(out.mode, mode) + self.assertNotEqual(out.size, im.size) + for mode in "1", "P", "L", "RGB", "I", "F": + rotate(mode) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py index 07a779664..284acd87c 100644 --- a/Tests/test_image_split.py +++ b/Tests/test_image_split.py @@ -1,49 +1,67 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image -def test_split(): - def split(mode): - layers = lena(mode).split() - return [(i.mode, i.size[0], i.size[1]) for i in layers] - assert_equal(split("1"), [('1', 128, 128)]) - assert_equal(split("L"), [('L', 128, 128)]) - assert_equal(split("I"), [('I', 128, 128)]) - assert_equal(split("F"), [('F', 128, 128)]) - assert_equal(split("P"), [('P', 128, 128)]) - assert_equal(split("RGB"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) - assert_equal(split("RGBA"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) - assert_equal(split("CMYK"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) - assert_equal(split("YCbCr"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) -def test_split_merge(): - def split_merge(mode): - return Image.merge(mode, lena(mode).split()) - assert_image_equal(lena("1"), split_merge("1")) - assert_image_equal(lena("L"), split_merge("L")) - assert_image_equal(lena("I"), split_merge("I")) - assert_image_equal(lena("F"), split_merge("F")) - assert_image_equal(lena("P"), split_merge("P")) - assert_image_equal(lena("RGB"), split_merge("RGB")) - assert_image_equal(lena("RGBA"), split_merge("RGBA")) - assert_image_equal(lena("CMYK"), split_merge("CMYK")) - assert_image_equal(lena("YCbCr"), split_merge("YCbCr")) +class TestImageSplit(PillowTestCase): -def test_split_open(): - codecs = dir(Image.core) + def test_split(self): + def split(mode): + layers = lena(mode).split() + return [(i.mode, i.size[0], i.size[1]) for i in layers] + self.assertEqual(split("1"), [('1', 128, 128)]) + self.assertEqual(split("L"), [('L', 128, 128)]) + self.assertEqual(split("I"), [('I', 128, 128)]) + self.assertEqual(split("F"), [('F', 128, 128)]) + self.assertEqual(split("P"), [('P', 128, 128)]) + self.assertEqual( + split("RGB"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + self.assertEqual( + split("RGBA"), + [('L', 128, 128), ('L', 128, 128), + ('L', 128, 128), ('L', 128, 128)]) + self.assertEqual( + split("CMYK"), + [('L', 128, 128), ('L', 128, 128), + ('L', 128, 128), ('L', 128, 128)]) + self.assertEqual( + split("YCbCr"), + [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) - if 'zip_encoder' in codecs: - file = tempfile("temp.png") - else: - file = tempfile("temp.pcx") + def test_split_merge(self): + def split_merge(mode): + return Image.merge(mode, lena(mode).split()) + self.assert_image_equal(lena("1"), split_merge("1")) + self.assert_image_equal(lena("L"), split_merge("L")) + self.assert_image_equal(lena("I"), split_merge("I")) + self.assert_image_equal(lena("F"), split_merge("F")) + self.assert_image_equal(lena("P"), split_merge("P")) + self.assert_image_equal(lena("RGB"), split_merge("RGB")) + self.assert_image_equal(lena("RGBA"), split_merge("RGBA")) + self.assert_image_equal(lena("CMYK"), split_merge("CMYK")) + self.assert_image_equal(lena("YCbCr"), split_merge("YCbCr")) - def split_open(mode): - lena(mode).save(file) - im = Image.open(file) - return len(im.split()) - assert_equal(split_open("1"), 1) - assert_equal(split_open("L"), 1) - assert_equal(split_open("P"), 1) - assert_equal(split_open("RGB"), 3) - if 'zip_encoder' in codecs: - assert_equal(split_open("RGBA"), 4) + def test_split_open(self): + codecs = dir(Image.core) + + if 'zip_encoder' in codecs: + file = self.tempfile("temp.png") + else: + file = self.tempfile("temp.pcx") + + def split_open(mode): + lena(mode).save(file) + im = Image.open(file) + return len(im.split()) + self.assertEqual(split_open("1"), 1) + self.assertEqual(split_open("L"), 1) + self.assertEqual(split_open("P"), 1) + self.assertEqual(split_open("RGB"), 3) + if 'zip_encoder' in codecs: + self.assertEqual(split_open("RGBA"), 4) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index 871dd1f54..6b33da318 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -1,36 +1,43 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena -from PIL import Image -def test_sanity(): +class TestImageThumbnail(PillowTestCase): - im = lena() - im.thumbnail((100, 100)) + def test_sanity(self): - assert_image(im, im.mode, (100, 100)) + im = lena() + im.thumbnail((100, 100)) -def test_aspect(): + self.assert_image(im, im.mode, (100, 100)) - im = lena() - im.thumbnail((100, 100)) - assert_image(im, im.mode, (100, 100)) + def test_aspect(self): - im = lena().resize((128, 256)) - im.thumbnail((100, 100)) - assert_image(im, im.mode, (50, 100)) + im = lena() + im.thumbnail((100, 100)) + self.assert_image(im, im.mode, (100, 100)) - im = lena().resize((128, 256)) - im.thumbnail((50, 100)) - assert_image(im, im.mode, (50, 100)) + im = lena().resize((128, 256)) + im.thumbnail((100, 100)) + self.assert_image(im, im.mode, (50, 100)) - im = lena().resize((256, 128)) - im.thumbnail((100, 100)) - assert_image(im, im.mode, (100, 50)) + im = lena().resize((128, 256)) + im.thumbnail((50, 100)) + self.assert_image(im, im.mode, (50, 100)) - im = lena().resize((256, 128)) - im.thumbnail((100, 50)) - assert_image(im, im.mode, (100, 50)) + im = lena().resize((256, 128)) + im.thumbnail((100, 100)) + self.assert_image(im, im.mode, (100, 50)) - im = lena().resize((128, 128)) - im.thumbnail((100, 100)) - assert_image(im, im.mode, (100, 100)) + im = lena().resize((256, 128)) + im.thumbnail((100, 50)) + self.assert_image(im, im.mode, (100, 50)) + + im = lena().resize((128, 128)) + im.thumbnail((100, 100)) + self.assert_image(im, im.mode, (100, 100)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py index 6fb10dd53..93f01c9de 100644 --- a/Tests/test_image_tobitmap.py +++ b/Tests/test_image_tobitmap.py @@ -1,15 +1,22 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena, fromstring -from PIL import Image -def test_sanity(): +class TestImageToBitmap(PillowTestCase): - assert_exception(ValueError, lambda: lena().tobitmap()) - assert_no_exception(lambda: lena().convert("1").tobitmap()) + def test_sanity(self): - im1 = lena().convert("1") + self.assertRaises(ValueError, lambda: lena().tobitmap()) + lena().convert("1").tobitmap() - bitmap = im1.tobitmap() + im1 = lena().convert("1") - assert_true(isinstance(bitmap, bytes)) - assert_image_equal(im1, fromstring(bitmap)) + bitmap = im1.tobitmap() + + self.assertIsInstance(bitmap, bytes) + self.assert_image_equal(im1, fromstring(bitmap)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_tobytes.py b/Tests/test_image_tobytes.py index d42399993..3be9128c1 100644 --- a/Tests/test_image_tobytes.py +++ b/Tests/test_image_tobytes.py @@ -1,7 +1,13 @@ -from tester import * +from helper import unittest, lena -from PIL import Image -def test_sanity(): - data = lena().tobytes() - assert_true(isinstance(data, bytes)) +class TestImageToBytes(unittest.TestCase): + + def test_sanity(self): + data = lena().tobytes() + self.assertTrue(isinstance(data, bytes)) + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index dd9b6fe5c..3f257fef2 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -1,116 +1,125 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image -def test_extent(): - im = lena('RGB') - (w,h) = im.size - transformed = im.transform(im.size, Image.EXTENT, - (0,0, - w//2,h//2), # ul -> lr - Image.BILINEAR) + +class TestImageTransform(PillowTestCase): + + def test_extent(self): + im = lena('RGB') + (w, h) = im.size + transformed = im.transform(im.size, Image.EXTENT, + (0, 0, + w//2, h//2), # ul -> lr + Image.BILINEAR) + + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h)) + + # undone -- precision? + self.assert_image_similar(transformed, scaled, 10) + + def test_quad(self): + # one simple quad transform, equivalent to scale & crop upper left quad + im = lena('RGB') + (w, h) = im.size + transformed = im.transform(im.size, Image.QUAD, + (0, 0, 0, h//2, + # ul -> ccw around quad: + w//2, h//2, w//2, 0), + Image.BILINEAR) + + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h)) + + self.assert_image_equal(transformed, scaled) + + def test_mesh(self): + # this should be a checkerboard of halfsized lenas in ul, lr + im = lena('RGBA') + (w, h) = im.size + transformed = im.transform(im.size, Image.MESH, + [((0, 0, w//2, h//2), # box + (0, 0, 0, h, + w, h, w, 0)), # ul -> ccw around quad + ((w//2, h//2, w, h), # box + (0, 0, 0, h, + w, h, w, 0))], # ul -> ccw around quad + Image.BILINEAR) + + # transformed.save('transformed.png') + + scaled = im.resize((w//2, h//2), Image.BILINEAR) + + checker = Image.new('RGBA', im.size) + checker.paste(scaled, (0, 0)) + checker.paste(scaled, (w//2, h//2)) + + self.assert_image_equal(transformed, checker) + + # now, check to see that the extra area is (0, 0, 0, 0) + blank = Image.new('RGBA', (w//2, h//2), (0, 0, 0, 0)) + + self.assert_image_equal(blank, transformed.crop((w//2, 0, w, h//2))) + self.assert_image_equal(blank, transformed.crop((0, h//2, w//2, h))) + + def _test_alpha_premult(self, op): + # create image with half white, half black, + # with the black half transparent. + # do op, + # there should be no darkness in the white section. + im = Image.new('RGBA', (10, 10), (0, 0, 0, 0)) + im2 = Image.new('RGBA', (5, 10), (255, 255, 255, 255)) + im.paste(im2, (0, 0)) + + im = op(im, (40, 10)) + im_background = Image.new('RGB', (40, 10), (255, 255, 255)) + im_background.paste(im, (0, 0), im) + + hist = im_background.histogram() + self.assertEqual(40*10, hist[-1]) + + def test_alpha_premult_resize(self): + + def op(im, sz): + return im.resize(sz, Image.LINEAR) + + self._test_alpha_premult(op) + + def test_alpha_premult_transform(self): + + def op(im, sz): + (w, h) = im.size + return im.transform(sz, Image.EXTENT, + (0, 0, + w, h), + Image.BILINEAR) + + self._test_alpha_premult(op) + + def test_blank_fill(self): + # attempting to hit + # https://github.com/python-pillow/Pillow/issues/254 reported + # + # issue is that transforms with transparent overflow area + # contained junk from previous images, especially on systems with + # constrained memory. So, attempt to fill up memory with a + # pattern, free it, and then run the mesh test again. Using a 1Mp + # image with 4 bands, for 4 megs of data allocated, x 64. OMM (64 + # bit 12.04 VM with 512 megs available, this fails with Pillow < + # a0eaf06cc5f62a6fb6de556989ac1014ff3348ea + # + # Running by default, but I'd totally understand not doing it in + # the future + + foo = [Image.new('RGBA', (1024, 1024), (a, a, a, a)) + for a in range(1, 65)] + + # Yeah. Watch some JIT optimize this out. + foo = None + + self.test_mesh() - scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) +if __name__ == '__main__': + unittest.main() - assert_image_similar(transformed, scaled, 10) # undone -- precision? - -def test_quad(): - # one simple quad transform, equivalent to scale & crop upper left quad - im = lena('RGB') - (w,h) = im.size - transformed = im.transform(im.size, Image.QUAD, - (0,0,0,h//2, - w//2,h//2,w//2,0), # ul -> ccw around quad - Image.BILINEAR) - - scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) - - assert_image_equal(transformed, scaled) - -def test_mesh(): - # this should be a checkerboard of halfsized lenas in ul, lr - im = lena('RGBA') - (w,h) = im.size - transformed = im.transform(im.size, Image.MESH, - [((0,0,w//2,h//2), # box - (0,0,0,h, - w,h,w,0)), # ul -> ccw around quad - ((w//2,h//2,w,h), # box - (0,0,0,h, - w,h,w,0))], # ul -> ccw around quad - Image.BILINEAR) - - #transformed.save('transformed.png') - - scaled = im.resize((w//2, h//2), Image.BILINEAR) - - checker = Image.new('RGBA', im.size) - checker.paste(scaled, (0,0)) - checker.paste(scaled, (w//2,h//2)) - - assert_image_equal(transformed, checker) - - # now, check to see that the extra area is (0,0,0,0) - blank = Image.new('RGBA', (w//2,h//2), (0,0,0,0)) - - assert_image_equal(blank, transformed.crop((w//2,0,w,h//2))) - assert_image_equal(blank, transformed.crop((0,h//2,w//2,h))) - -def _test_alpha_premult(op): - # create image with half white, half black, with the black half transparent. - # do op, - # there should be no darkness in the white section. - im = Image.new('RGBA', (10,10), (0,0,0,0)); - im2 = Image.new('RGBA', (5,10), (255,255,255,255)); - im.paste(im2, (0,0)) - - im = op(im, (40,10)) - im_background = Image.new('RGB', (40,10), (255,255,255)) - im_background.paste(im, (0,0), im) - - hist = im_background.histogram() - assert_equal(40*10, hist[-1]) - - -def test_alpha_premult_resize(): - - def op (im, sz): - return im.resize(sz, Image.LINEAR) - - _test_alpha_premult(op) - -def test_alpha_premult_transform(): - - def op(im, sz): - (w,h) = im.size - return im.transform(sz, Image.EXTENT, - (0,0, - w,h), - Image.BILINEAR) - - _test_alpha_premult(op) - - -def test_blank_fill(): - # attempting to hit - # https://github.com/python-pillow/Pillow/issues/254 reported - # - # issue is that transforms with transparent overflow area - # contained junk from previous images, especially on systems with - # constrained memory. So, attempt to fill up memory with a - # pattern, free it, and then run the mesh test again. Using a 1Mp - # image with 4 bands, for 4 megs of data allocated, x 64. OMM (64 - # bit 12.04 VM with 512 megs available, this fails with Pillow < - # a0eaf06cc5f62a6fb6de556989ac1014ff3348ea - # - # Running by default, but I'd totally understand not doing it in - # the future - - foo = [Image.new('RGBA',(1024,1024), (a,a,a,a)) - for a in range(1,65)] - - # Yeah. Watch some JIT optimize this out. - foo = None - - test_mesh() +# End of file diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 43b3ef9d3..ec83aa3a6 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image @@ -8,27 +8,37 @@ ROTATE_90 = Image.ROTATE_90 ROTATE_180 = Image.ROTATE_180 ROTATE_270 = Image.ROTATE_270 -def test_sanity(): - im = lena() +class TestImageTranspose(PillowTestCase): - assert_no_exception(lambda: im.transpose(FLIP_LEFT_RIGHT)) - assert_no_exception(lambda: im.transpose(FLIP_TOP_BOTTOM)) + def test_sanity(self): - assert_no_exception(lambda: im.transpose(ROTATE_90)) - assert_no_exception(lambda: im.transpose(ROTATE_180)) - assert_no_exception(lambda: im.transpose(ROTATE_270)) + im = lena() -def test_roundtrip(): + im.transpose(FLIP_LEFT_RIGHT) + im.transpose(FLIP_TOP_BOTTOM) - im = lena() + im.transpose(ROTATE_90) + im.transpose(ROTATE_180) + im.transpose(ROTATE_270) - def transpose(first, second): - return im.transpose(first).transpose(second) + def test_roundtrip(self): - assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) - assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) + im = lena() - assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) - assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) + def transpose(first, second): + return im.transpose(first).transpose(second) + self.assert_image_equal( + im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) + self.assert_image_equal( + im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) + + self.assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) + self.assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index 16eaaf55e..fe377f864 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -1,56 +1,74 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image from PIL import ImageChops -def test_sanity(): - im = lena("L") +class TestImageChops(PillowTestCase): - ImageChops.constant(im, 128) - ImageChops.duplicate(im) - ImageChops.invert(im) - ImageChops.lighter(im, im) - ImageChops.darker(im, im) - ImageChops.difference(im, im) - ImageChops.multiply(im, im) - ImageChops.screen(im, im) + def test_sanity(self): - ImageChops.add(im, im) - ImageChops.add(im, im, 2.0) - ImageChops.add(im, im, 2.0, 128) - ImageChops.subtract(im, im) - ImageChops.subtract(im, im, 2.0) - ImageChops.subtract(im, im, 2.0, 128) + im = lena("L") - ImageChops.add_modulo(im, im) - ImageChops.subtract_modulo(im, im) + ImageChops.constant(im, 128) + ImageChops.duplicate(im) + ImageChops.invert(im) + ImageChops.lighter(im, im) + ImageChops.darker(im, im) + ImageChops.difference(im, im) + ImageChops.multiply(im, im) + ImageChops.screen(im, im) - ImageChops.blend(im, im, 0.5) - ImageChops.composite(im, im, im) + ImageChops.add(im, im) + ImageChops.add(im, im, 2.0) + ImageChops.add(im, im, 2.0, 128) + ImageChops.subtract(im, im) + ImageChops.subtract(im, im, 2.0) + ImageChops.subtract(im, im, 2.0, 128) - ImageChops.offset(im, 10) - ImageChops.offset(im, 10, 20) + ImageChops.add_modulo(im, im) + ImageChops.subtract_modulo(im, im) -def test_logical(): + ImageChops.blend(im, im, 0.5) + ImageChops.composite(im, im, im) - def table(op, a, b): - out = [] - for x in (a, b): - imx = Image.new("1", (1, 1), x) - for y in (a, b): - imy = Image.new("1", (1, 1), y) - out.append(op(imx, imy).getpixel((0, 0))) - return tuple(out) + ImageChops.offset(im, 10) + ImageChops.offset(im, 10, 20) - assert_equal(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) - assert_equal(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) - assert_equal(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) + def test_logical(self): - assert_equal(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) - assert_equal(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) - assert_equal(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + def table(op, a, b): + out = [] + for x in (a, b): + imx = Image.new("1", (1, 1), x) + for y in (a, b): + imy = Image.new("1", (1, 1), y) + out.append(op(imx, imy).getpixel((0, 0))) + return tuple(out) - assert_equal(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) - assert_equal(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) - assert_equal(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) + self.assertEqual( + table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) + self.assertEqual( + table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) + self.assertEqual( + table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) + + self.assertEqual( + table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) + self.assertEqual( + table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) + self.assertEqual( + table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + + self.assertEqual( + table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) + self.assertEqual( + table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) + self.assertEqual( + table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index f52101eb1..d4432d9be 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -1,203 +1,214 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image + try: from PIL import ImageCms ImageCms.core.profile_open -except ImportError: - skip() +except ImportError as v: + # Skipped via setUp() + pass + SRGB = "Tests/icc/sRGB.icm" -def test_sanity(): +class TestImageCms(PillowTestCase): - # basic smoke test. - # this mostly follows the cms_test outline. + def setUp(self): + try: + from PIL import ImageCms + except ImportError as v: + self.skipTest(v) - v = ImageCms.versions() # should return four strings - assert_equal(v[0], '1.0.0 pil') - assert_equal(list(map(type, v)), [str, str, str, str]) + def test_sanity(self): - # internal version number - assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") + # basic smoke test. + # this mostly follows the cms_test outline. - i = ImageCms.profileToProfile(lena(), SRGB, SRGB) - assert_image(i, "RGB", (128, 128)) + v = ImageCms.versions() # should return four strings + self.assertEqual(v[0], '1.0.0 pil') + self.assertEqual(list(map(type, v)), [str, str, str, str]) - i = lena() - ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) - assert_image(i, "RGB", (128, 128)) + # internal version number + self.assertRegexpMatches(ImageCms.core.littlecms_version, "\d+\.\d+$") - t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") - i = ImageCms.applyTransform(lena(), t) - assert_image(i, "RGB", (128, 128)) + i = ImageCms.profileToProfile(lena(), SRGB, SRGB) + self.assert_image(i, "RGB", (128, 128)) - i = lena() - t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") - ImageCms.applyTransform(lena(), t, inPlace=True) - assert_image(i, "RGB", (128, 128)) + i = lena() + ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) + self.assert_image(i, "RGB", (128, 128)) - p = ImageCms.createProfile("sRGB") - o = ImageCms.getOpenProfile(SRGB) - t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") - i = ImageCms.applyTransform(lena(), t) - assert_image(i, "RGB", (128, 128)) + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + self.assert_image(i, "RGB", (128, 128)) - t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") - assert_equal(t.inputMode, "RGB") - assert_equal(t.outputMode, "RGB") - i = ImageCms.applyTransform(lena(), t) - assert_image(i, "RGB", (128, 128)) + i = lena() + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + ImageCms.applyTransform(lena(), t, inPlace=True) + self.assert_image(i, "RGB", (128, 128)) - # test PointTransform convenience API - lena().point(t) + p = ImageCms.createProfile("sRGB") + o = ImageCms.getOpenProfile(SRGB) + t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + self.assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") + self.assertEqual(t.inputMode, "RGB") + self.assertEqual(t.outputMode, "RGB") + i = ImageCms.applyTransform(lena(), t) + self.assert_image(i, "RGB", (128, 128)) + + # test PointTransform convenience API + lena().point(t) + + def test_name(self): + # get profile information for file + self.assertEqual( + ImageCms.getProfileName(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + def test_info(self): + self.assertEqual( + ImageCms.getProfileInfo(SRGB).splitlines(), [ + 'sRGB IEC61966-2.1', '', + 'Copyright (c) 1998 Hewlett-Packard Company', '']) + + def test_copyright(self): + self.assertEqual( + ImageCms.getProfileCopyright(SRGB).strip(), + 'Copyright (c) 1998 Hewlett-Packard Company') + + def test_manufacturer(self): + self.assertEqual( + ImageCms.getProfileManufacturer(SRGB).strip(), + 'IEC http://www.iec.ch') + + def test_model(self): + self.assertEqual( + ImageCms.getProfileModel(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + def test_description(self): + self.assertEqual( + ImageCms.getProfileDescription(SRGB).strip(), + 'sRGB IEC61966-2.1') + + def test_intent(self): + self.assertEqual(ImageCms.getDefaultIntent(SRGB), 0) + self.assertEqual(ImageCms.isIntentSupported( + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + def test_profile_object(self): + # same, using profile object + p = ImageCms.createProfile("sRGB") + # self.assertEqual(ImageCms.getProfileName(p).strip(), + # 'sRGB built-in - (lcms internal)') + # self.assertEqual(ImageCms.getProfileInfo(p).splitlines(), + # ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) + self.assertEqual(ImageCms.getDefaultIntent(p), 0) + self.assertEqual(ImageCms.isIntentSupported( + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + def test_extensions(self): + # extensions + from io import BytesIO + i = Image.open("Tests/images/rgb.jpg") + p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) + self.assertEqual( + ImageCms.getProfileName(p).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + def test_exceptions(self): + # the procedural pyCMS API uses PyCMSError for all sorts of errors + self.assertRaises( + ImageCms.PyCMSError, + lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) + self.assertRaises( + ImageCms.PyCMSError, + lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) + self.assertRaises( + ImageCms.PyCMSError, + lambda: ImageCms.getProfileName(None)) + self.assertRaises( + ImageCms.PyCMSError, + lambda: ImageCms.isIntentSupported(SRGB, None, None)) + + def test_display_profile(self): + # try fetching the profile for the current display device + ImageCms.get_display_profile() + + def test_lab_color_profile(self): + ImageCms.createProfile("LAB", 5000) + ImageCms.createProfile("LAB", 6500) + + def test_simple_lab(self): + i = Image.new('RGB', (10, 10), (128, 128, 128)) + + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + i_lab = ImageCms.applyTransform(i, t) + + self.assertEqual(i_lab.mode, 'LAB') + + k = i_lab.getpixel((0, 0)) + # not a linear luminance map. so L != 128: + self.assertEqual(k, (137, 128, 128)) + + L = i_lab.getdata(0) + a = i_lab.getdata(1) + b = i_lab.getdata(2) + + self.assertEqual(list(L), [137] * 100) + self.assertEqual(list(a), [128] * 100) + self.assertEqual(list(b), [128] * 100) + + def test_lab_color(self): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + # Need to add a type mapping for some PIL type to TYPE_Lab_8 in + # findLCMSType, and have that mapping work back to a PIL mode + # (likely RGB). + i = ImageCms.applyTransform(lena(), t) + self.assert_image(i, "LAB", (128, 128)) + + # i.save('temp.lab.tif') # visually verified vs PS. + + target = Image.open('Tests/images/lena.Lab.tif') + + self.assert_image_similar(i, target, 30) + + def test_lab_srgb(self): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + img = Image.open('Tests/images/lena.Lab.tif') + + img_srgb = ImageCms.applyTransform(img, t) + + # img_srgb.save('temp.srgb.tif') # visually verified vs ps. + + self.assert_image_similar(lena(), img_srgb, 30) + + def test_lab_roundtrip(self): + # check to see if we're at least internally consistent. + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + i = ImageCms.applyTransform(lena(), t) + out = ImageCms.applyTransform(i, t2) + + self.assert_image_similar(lena(), out, 2) -def test_name(): - # get profile information for file - assert_equal(ImageCms.getProfileName(SRGB).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') +if __name__ == '__main__': + unittest.main() - -def test_info(): - assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), - ['sRGB IEC61966-2.1', '', - 'Copyright (c) 1998 Hewlett-Packard Company', '']) - - -def test_copyright(): - assert_equal(ImageCms.getProfileCopyright(SRGB).strip(), - 'Copyright (c) 1998 Hewlett-Packard Company') - - -def test_manufacturer(): - assert_equal(ImageCms.getProfileManufacturer(SRGB).strip(), - 'IEC http://www.iec.ch') - - -def test_model(): - assert_equal(ImageCms.getProfileModel(SRGB).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') - - -def test_description(): - assert_equal(ImageCms.getProfileDescription(SRGB).strip(), - 'sRGB IEC61966-2.1') - - -def test_intent(): - assert_equal(ImageCms.getDefaultIntent(SRGB), 0) - assert_equal(ImageCms.isIntentSupported( - SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) - - -def test_profile_object(): - # same, using profile object - p = ImageCms.createProfile("sRGB") -# assert_equal(ImageCms.getProfileName(p).strip(), -# 'sRGB built-in - (lcms internal)') -# assert_equal(ImageCms.getProfileInfo(p).splitlines(), -# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) - assert_equal(ImageCms.getDefaultIntent(p), 0) - assert_equal(ImageCms.isIntentSupported( - p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) - - -def test_extensions(): - # extensions - i = Image.open("Tests/images/rgb.jpg") - p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) - assert_equal(ImageCms.getProfileName(p).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') - - -def test_exceptions(): - # the procedural pyCMS API uses PyCMSError for all sorts of errors - assert_exception( - ImageCms.PyCMSError, - lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) - assert_exception( - ImageCms.PyCMSError, - lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) - assert_exception( - ImageCms.PyCMSError, - lambda: ImageCms.getProfileName(None)) - assert_exception( - ImageCms.PyCMSError, - lambda: ImageCms.isIntentSupported(SRGB, None, None)) - - -def test_display_profile(): - # try fetching the profile for the current display device - assert_no_exception(lambda: ImageCms.get_display_profile()) - - -def test_lab_color_profile(): - ImageCms.createProfile("LAB", 5000) - ImageCms.createProfile("LAB", 6500) - - -def test_simple_lab(): - i = Image.new('RGB', (10, 10), (128, 128, 128)) - - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - - i_lab = ImageCms.applyTransform(i, t) - - assert_equal(i_lab.mode, 'LAB') - - k = i_lab.getpixel((0, 0)) - assert_equal(k, (137, 128, 128)) # not a linear luminance map. so L != 128 - - L = i_lab.getdata(0) - a = i_lab.getdata(1) - b = i_lab.getdata(2) - - assert_equal(list(L), [137] * 100) - assert_equal(list(a), [128] * 100) - assert_equal(list(b), [128] * 100) - - -def test_lab_color(): - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - # Need to add a type mapping for some PIL type to TYPE_Lab_8 in - # findLCMSType, and have that mapping work back to a PIL mode (likely RGB). - i = ImageCms.applyTransform(lena(), t) - assert_image(i, "LAB", (128, 128)) - - # i.save('temp.lab.tif') # visually verified vs PS. - - target = Image.open('Tests/images/lena.Lab.tif') - - assert_image_similar(i, target, 30) - - -def test_lab_srgb(): - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") - - img = Image.open('Tests/images/lena.Lab.tif') - - img_srgb = ImageCms.applyTransform(img, t) - - # img_srgb.save('temp.srgb.tif') # visually verified vs ps. - - assert_image_similar(lena(), img_srgb, 30) - - -def test_lab_roundtrip(): - # check to see if we're at least internally consistent. - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - - t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") - - i = ImageCms.applyTransform(lena(), t) - out = ImageCms.applyTransform(i, t2) - - assert_image_similar(lena(), out, 2) +# End of file diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py index c67c20255..fce64876b 100644 --- a/Tests/test_imagecolor.py +++ b/Tests/test_imagecolor.py @@ -1,54 +1,71 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image from PIL import ImageColor -# -------------------------------------------------------------------- -# sanity -assert_equal((255, 0, 0), ImageColor.getrgb("#f00")) -assert_equal((255, 0, 0), ImageColor.getrgb("#ff0000")) -assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) -assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)")) -assert_equal((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) -assert_equal((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) -assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)")) -assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) -assert_equal((255, 0, 0), ImageColor.getrgb("red")) +class TestImageColor(PillowTestCase): -# -------------------------------------------------------------------- -# look for rounding errors (based on code by Tim Hatch) + def test_sanity(self): + self.assertEqual((255, 0, 0), ImageColor.getrgb("#f00")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("#ff0000")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) + self.assertEqual((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)")) + self.assertEqual( + (255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("red")) -for color in list(ImageColor.colormap.keys()): - expected = Image.new("RGB", (1, 1), color).convert("L").getpixel((0, 0)) - actual = Image.new("L", (1, 1), color).getpixel((0, 0)) - assert_equal(expected, actual) + # look for rounding errors (based on code by Tim Hatch) + def test_rounding_errors(self): -assert_equal((0, 0, 0), ImageColor.getcolor("black", "RGB")) -assert_equal((255, 255, 255), ImageColor.getcolor("white", "RGB")) -assert_equal((0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB")) -Image.new("RGB", (1, 1), "white") + for color in list(ImageColor.colormap.keys()): + expected = Image.new( + "RGB", (1, 1), color).convert("L").getpixel((0, 0)) + actual = Image.new("L", (1, 1), color).getpixel((0, 0)) + self.assertEqual(expected, actual) -assert_equal((0, 0, 0, 255), ImageColor.getcolor("black", "RGBA")) -assert_equal((255, 255, 255, 255), ImageColor.getcolor("white", "RGBA")) -assert_equal((0, 255, 115, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGBA")) -Image.new("RGBA", (1, 1), "white") + self.assertEqual((0, 0, 0), ImageColor.getcolor("black", "RGB")) + self.assertEqual((255, 255, 255), ImageColor.getcolor("white", "RGB")) + self.assertEqual( + (0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB")) + Image.new("RGB", (1, 1), "white") -assert_equal(0, ImageColor.getcolor("black", "L")) -assert_equal(255, ImageColor.getcolor("white", "L")) -assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) -Image.new("L", (1, 1), "white") + self.assertEqual((0, 0, 0, 255), ImageColor.getcolor("black", "RGBA")) + self.assertEqual( + (255, 255, 255, 255), ImageColor.getcolor("white", "RGBA")) + self.assertEqual( + (0, 255, 115, 33), + ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGBA")) + Image.new("RGBA", (1, 1), "white") -assert_equal(0, ImageColor.getcolor("black", "1")) -assert_equal(255, ImageColor.getcolor("white", "1")) -# The following test is wrong, but is current behavior -# The correct result should be 255 due to the mode 1 -assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) -# Correct behavior -# assert_equal(255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) -Image.new("1", (1, 1), "white") + self.assertEqual(0, ImageColor.getcolor("black", "L")) + self.assertEqual(255, ImageColor.getcolor("white", "L")) + self.assertEqual( + 162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) + Image.new("L", (1, 1), "white") -assert_equal((0, 255), ImageColor.getcolor("black", "LA")) -assert_equal((255, 255), ImageColor.getcolor("white", "LA")) -assert_equal((162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) -Image.new("LA", (1, 1), "white") + self.assertEqual(0, ImageColor.getcolor("black", "1")) + self.assertEqual(255, ImageColor.getcolor("white", "1")) + # The following test is wrong, but is current behavior + # The correct result should be 255 due to the mode 1 + self.assertEqual( + 162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) + # Correct behavior + # self.assertEqual( + # 255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) + Image.new("1", (1, 1), "white") + + self.assertEqual((0, 255), ImageColor.getcolor("black", "LA")) + self.assertEqual((255, 255), ImageColor.getcolor("white", "LA")) + self.assertEqual( + (162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) + Image.new("LA", (1, 1), "white") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 7b682020e..a19ba78f8 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1,9 +1,11 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image from PIL import ImageColor from PIL import ImageDraw +import sys + # Image size w, h = 100, 100 @@ -22,249 +24,232 @@ points1 = [(10, 10), (20, 40), (30, 30)] points2 = [10, 10, 20, 40, 30, 30] -def test_sanity(): +class TestImageDraw(PillowTestCase): - im = lena("RGB").copy() + def test_sanity(self): + im = lena("RGB").copy() - draw = ImageDraw.ImageDraw(im) - draw = ImageDraw.Draw(im) + draw = ImageDraw.ImageDraw(im) + draw = ImageDraw.Draw(im) - draw.ellipse(list(range(4))) - draw.line(list(range(10))) - draw.polygon(list(range(100))) - draw.rectangle(list(range(4))) + draw.ellipse(list(range(4))) + draw.line(list(range(10))) + draw.polygon(list(range(100))) + draw.rectangle(list(range(4))) - success() + def test_deprecated(self): + im = lena().copy() + draw = ImageDraw.Draw(im) -def test_deprecated(): + self.assert_warning(DeprecationWarning, lambda: draw.setink(0)) + self.assert_warning(DeprecationWarning, lambda: draw.setfill(0)) - im = lena().copy() + def helper_arc(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) - draw = ImageDraw.Draw(im) + # Act + # FIXME Fill param should be named outline. + draw.arc(bbox, 0, 180) + del draw - assert_warning(DeprecationWarning, lambda: draw.setink(0)) - assert_warning(DeprecationWarning, lambda: draw.setfill(0)) + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_arc.png")) + def test_arc1(self): + self.helper_arc(bbox1) -def helper_arc(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) + def test_arc2(self): + self.helper_arc(bbox2) - # Act - # FIXME Fill param should be named outline. - draw.arc(bbox, 0, 180) - del draw + def test_bitmap(self): + # Arrange + small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_arc.png")) + # Act + draw.bitmap((10, 10), small) + del draw + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_bitmap.png")) -def test_arc1(): - helper_arc(bbox1) + def helper_chord(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + # Act + draw.chord(bbox, 0, 180, fill="red", outline="yellow") + del draw -def test_arc2(): - helper_arc(bbox2) + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_chord.png")) + def test_chord1(self): + self.helper_chord(bbox1) -def test_bitmap(): - # Arrange - small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) + def test_chord2(self): + self.helper_chord(bbox2) - # Act - draw.bitmap((10, 10), small) - del draw + def helper_ellipse(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png")) + # Act + draw.ellipse(bbox, fill="green", outline="blue") + del draw + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_ellipse.png")) -def helper_chord(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) + def test_ellipse1(self): + self.helper_ellipse(bbox1) - # Act - draw.chord(bbox, 0, 180, fill="red", outline="yellow") - del draw + def test_ellipse2(self): + self.helper_ellipse(bbox2) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_chord.png")) + def helper_line(self, points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + # Act + draw.line(points1, fill="yellow", width=2) + del draw -def test_chord1(): - helper_chord(bbox1) + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_line.png")) + def test_line1(self): + self.helper_line(points1) -def test_chord2(): - helper_chord(bbox2) + def test_line2(self): + self.helper_line(points2) + def helper_pieslice(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) -def helper_ellipse(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) + # Act + draw.pieslice(bbox, -90, 45, fill="white", outline="blue") + del draw - # Act - draw.ellipse(bbox, fill="green", outline="blue") - del draw + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_pieslice.png")) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_ellipse.png")) + def test_pieslice1(self): + self.helper_pieslice(bbox1) + def test_pieslice2(self): + self.helper_pieslice(bbox2) -def test_ellipse1(): - helper_ellipse(bbox1) + def helper_point(self, points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + # Act + draw.point(points1, fill="yellow") + del draw -def test_ellipse2(): - helper_ellipse(bbox2) + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_point.png")) + def test_point1(self): + self.helper_point(points1) -def helper_line(points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) + def test_point2(self): + self.helper_point(points2) - # Act - draw.line(points1, fill="yellow", width=2) - del draw + def helper_polygon(self, points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) + # Act + draw.polygon(points1, fill="red", outline="blue") + del draw + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_polygon.png")) -def test_line1(): - helper_line(points1) + def test_polygon1(self): + self.helper_polygon(points1) + def test_polygon2(self): + self.helper_polygon(points2) -def test_line2(): - helper_line(points2) + def helper_rectangle(self, bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + # Act + draw.rectangle(bbox, fill="black", outline="green") + del draw -def helper_pieslice(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_rectangle.png")) - # Act - draw.pieslice(bbox, -90, 45, fill="white", outline="blue") - del draw + def test_rectangle1(self): + self.helper_rectangle(bbox1) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_pieslice.png")) + def test_rectangle2(self): + self.helper_rectangle(bbox2) + def test_floodfill(self): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + draw.rectangle(bbox2, outline="yellow", fill="green") + centre_point = (int(w/2), int(h/2)) -def test_pieslice1(): - helper_pieslice(bbox1) + # Act + ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) + del draw + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_floodfill.png")) -def test_pieslice2(): - helper_pieslice(bbox2) + @unittest.skipIf(hasattr(sys, 'pypy_version_info'), + "Causes fatal RPython error on PyPy") + def test_floodfill_border(self): + # floodfill() is experimental + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + draw.rectangle(bbox2, outline="yellow", fill="green") + centre_point = (int(w/2), int(h/2)) -def helper_point(points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) + # Act + ImageDraw.floodfill( + im, centre_point, ImageColor.getrgb("red"), + border=ImageColor.getrgb("black")) + del draw - # Act - draw.point(points1, fill="yellow") - del draw + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_floodfill2.png")) - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png")) - - -def test_point1(): - helper_point(points1) - - -def test_point2(): - helper_point(points2) - - -def helper_polygon(points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.polygon(points1, fill="red", outline="blue") - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) - - -def test_polygon1(): - helper_polygon(points1) - - -def test_polygon2(): - helper_polygon(points2) - - -def helper_rectangle(bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.rectangle(bbox, fill="black", outline="green") - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) - - -def test_rectangle1(): - helper_rectangle(bbox1) - - -def test_rectangle2(): - helper_rectangle(bbox2) - - -def test_floodfill(): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - draw.rectangle(bbox2, outline="yellow", fill="green") - centre_point = (int(w/2), int(h/2)) - - # Act - ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill.png")) - - -def test_floodfill_border(): - # floodfill() is experimental - if hasattr(sys, 'pypy_version_info'): - # Causes fatal RPython error on PyPy - skip() - - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - draw.rectangle(bbox2, outline="yellow", fill="green") - centre_point = (int(w/2), int(h/2)) - - # Act - ImageDraw.floodfill( - im, centre_point, ImageColor.getrgb("red"), - border=ImageColor.getrgb("black")) - del draw - - # Assert - assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) +if __name__ == '__main__': + unittest.main() # End of file diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index 04f16bfa5..eec26d768 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -1,19 +1,28 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image from PIL import ImageEnhance -def test_sanity(): - # FIXME: assert_image - assert_no_exception(lambda: ImageEnhance.Color(lena()).enhance(0.5)) - assert_no_exception(lambda: ImageEnhance.Contrast(lena()).enhance(0.5)) - assert_no_exception(lambda: ImageEnhance.Brightness(lena()).enhance(0.5)) - assert_no_exception(lambda: ImageEnhance.Sharpness(lena()).enhance(0.5)) +class TestImageEnhance(PillowTestCase): -def test_crash(): + def test_sanity(self): - # crashes on small images - im = Image.new("RGB", (1, 1)) - assert_no_exception(lambda: ImageEnhance.Sharpness(im).enhance(0.5)) + # FIXME: assert_image + # Implicit asserts no exception: + ImageEnhance.Color(lena()).enhance(0.5) + ImageEnhance.Contrast(lena()).enhance(0.5) + ImageEnhance.Brightness(lena()).enhance(0.5) + ImageEnhance.Sharpness(lena()).enhance(0.5) + def test_crash(self): + + # crashes on small images + im = Image.new("RGB", (1, 1)) + ImageEnhance.Sharpness(im).enhance(0.5) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index e3992828a..849767195 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -1,82 +1,93 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena, fromstring, tostring + +from io import BytesIO from PIL import Image from PIL import ImageFile from PIL import EpsImagePlugin + codecs = dir(Image.core) # save original block sizes MAXBLOCK = ImageFile.MAXBLOCK SAFEBLOCK = ImageFile.SAFEBLOCK -def test_parser(): - def roundtrip(format): +class TestImagePutData(PillowTestCase): - im = lena("L").resize((1000, 1000)) - if format in ("MSP", "XBM"): - im = im.convert("1") + def test_parser(self): - file = BytesIO() + def roundtrip(format): - im.save(file, format) + im = lena("L").resize((1000, 1000)) + if format in ("MSP", "XBM"): + im = im.convert("1") - data = file.getvalue() + file = BytesIO() - parser = ImageFile.Parser() - parser.feed(data) - imOut = parser.close() + im.save(file, format) - return im, imOut + data = file.getvalue() + + parser = ImageFile.Parser() + parser.feed(data) + imOut = parser.close() + + return im, imOut + + self.assert_image_equal(*roundtrip("BMP")) + self.assert_image_equal(*roundtrip("GIF")) + self.assert_image_equal(*roundtrip("IM")) + self.assert_image_equal(*roundtrip("MSP")) + if "zip_encoder" in codecs: + try: + # force multiple blocks in PNG driver + ImageFile.MAXBLOCK = 8192 + self.assert_image_equal(*roundtrip("PNG")) + finally: + ImageFile.MAXBLOCK = MAXBLOCK + self.assert_image_equal(*roundtrip("PPM")) + self.assert_image_equal(*roundtrip("TIFF")) + self.assert_image_equal(*roundtrip("XBM")) + self.assert_image_equal(*roundtrip("TGA")) + self.assert_image_equal(*roundtrip("PCX")) + + if EpsImagePlugin.has_ghostscript(): + im1, im2 = roundtrip("EPS") + # EPS comes back in RGB: + self.assert_image_similar(im1, im2.convert('L'), 20) + + if "jpeg_encoder" in codecs: + im1, im2 = roundtrip("JPEG") # lossy compression + self.assert_image(im1, im2.mode, im2.size) + + self.assertRaises(IOError, lambda: roundtrip("PDF")) + + def test_ico(self): + with open('Tests/images/python.ico', 'rb') as f: + data = f.read() + p = ImageFile.Parser() + p.feed(data) + self.assertEqual((48, 48), p.image.size) + + def test_safeblock(self): + + im1 = lena() + + if "zip_encoder" not in codecs: + self.skipTest("PNG (zlib) encoder not available") - assert_image_equal(*roundtrip("BMP")) - assert_image_equal(*roundtrip("GIF")) - assert_image_equal(*roundtrip("IM")) - assert_image_equal(*roundtrip("MSP")) - if "zip_encoder" in codecs: try: - # force multiple blocks in PNG driver - ImageFile.MAXBLOCK = 8192 - assert_image_equal(*roundtrip("PNG")) + ImageFile.SAFEBLOCK = 1 + im2 = fromstring(tostring(im1, "PNG")) finally: - ImageFile.MAXBLOCK = MAXBLOCK - assert_image_equal(*roundtrip("PPM")) - assert_image_equal(*roundtrip("TIFF")) - assert_image_equal(*roundtrip("XBM")) - assert_image_equal(*roundtrip("TGA")) - assert_image_equal(*roundtrip("PCX")) + ImageFile.SAFEBLOCK = SAFEBLOCK - if EpsImagePlugin.has_ghostscript(): - im1, im2 = roundtrip("EPS") - assert_image_similar(im1, im2.convert('L'),20) # EPS comes back in RGB + self.assert_image_equal(im1, im2) - if "jpeg_encoder" in codecs: - im1, im2 = roundtrip("JPEG") # lossy compression - assert_image(im1, im2.mode, im2.size) - # XXX Why assert exception and why does it fail? - # https://github.com/python-pillow/Pillow/issues/78 - #assert_exception(IOError, lambda: roundtrip("PDF")) +if __name__ == '__main__': + unittest.main() -def test_ico(): - with open('Tests/images/python.ico', 'rb') as f: - data = f.read() - p = ImageFile.Parser() - p.feed(data) - assert_equal((48,48), p.image.size) - -def test_safeblock(): - - im1 = lena() - - if "zip_encoder" not in codecs: - skip("PNG (zlib) encoder not available") - - try: - ImageFile.SAFEBLOCK = 1 - im2 = fromstring(tostring(im1, "PNG")) - finally: - ImageFile.SAFEBLOCK = SAFEBLOCK - - assert_image_equal(im1, im2) +# End of file diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py index c63f07bb0..791207dca 100644 --- a/Tests/test_imagefileio.py +++ b/Tests/test_imagefileio.py @@ -1,24 +1,33 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena, tostring from PIL import Image from PIL import ImageFileIO -def test_fileio(): - class DumbFile: - def __init__(self, data): - self.data = data - def read(self, bytes=None): - assert_equal(bytes, None) - return self.data - def close(self): - pass +class TestImageFileIo(PillowTestCase): - im1 = lena() + def test_fileio(self): - io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) + class DumbFile: + def __init__(self, data): + self.data = data - im2 = Image.open(io) - assert_image_equal(im1, im2) + def read(self, bytes=None): + assert(bytes is None) + return self.data + + def close(self): + pass + + im1 = lena() + + io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) + + im2 = Image.open(io) + self.assert_image_equal(im1, im2) +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagefilter.py b/Tests/test_imagefilter.py index 214f88024..3dcb1d14f 100644 --- a/Tests/test_imagefilter.py +++ b/Tests/test_imagefilter.py @@ -1,31 +1,37 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule -from PIL import Image from PIL import ImageFilter -def test_sanity(): - # see test_image_filter for more tests - assert_no_exception(lambda: ImageFilter.MaxFilter) - assert_no_exception(lambda: ImageFilter.MedianFilter) - assert_no_exception(lambda: ImageFilter.MinFilter) - assert_no_exception(lambda: ImageFilter.ModeFilter) - assert_no_exception(lambda: ImageFilter.Kernel((3, 3), list(range(9)))) - assert_no_exception(lambda: ImageFilter.GaussianBlur) - assert_no_exception(lambda: ImageFilter.GaussianBlur(5)) - assert_no_exception(lambda: ImageFilter.UnsharpMask) - assert_no_exception(lambda: ImageFilter.UnsharpMask(10)) +class TestImageFilter(PillowTestCase): - assert_no_exception(lambda: ImageFilter.BLUR) - assert_no_exception(lambda: ImageFilter.CONTOUR) - assert_no_exception(lambda: ImageFilter.DETAIL) - assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE) - assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE_MORE) - assert_no_exception(lambda: ImageFilter.EMBOSS) - assert_no_exception(lambda: ImageFilter.FIND_EDGES) - assert_no_exception(lambda: ImageFilter.SMOOTH) - assert_no_exception(lambda: ImageFilter.SMOOTH_MORE) - assert_no_exception(lambda: ImageFilter.SHARPEN) + def test_sanity(self): + # see test_image_filter for more tests + + # Check these run. Exceptions cause failures. + ImageFilter.MaxFilter + ImageFilter.MedianFilter + ImageFilter.MinFilter + ImageFilter.ModeFilter + ImageFilter.Kernel((3, 3), list(range(9))) + ImageFilter.GaussianBlur + ImageFilter.GaussianBlur(5) + ImageFilter.UnsharpMask + ImageFilter.UnsharpMask(10) + + ImageFilter.BLUR + ImageFilter.CONTOUR + ImageFilter.DETAIL + ImageFilter.EDGE_ENHANCE + ImageFilter.EDGE_ENHANCE_MORE + ImageFilter.EMBOSS + ImageFilter.FIND_EDGES + ImageFilter.SMOOTH + ImageFilter.SMOOTH_MORE + ImageFilter.SHARPEN +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 9ac2cdd89..927c80bee 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1,135 +1,145 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image +from PIL import ImageDraw from io import BytesIO import os +font_path = "Tests/fonts/FreeMono.ttf" +font_size = 20 + + try: from PIL import ImageFont - ImageFont.core.getfont # check if freetype is available + ImageFont.core.getfont # check if freetype is available + + class TestImageFont(PillowTestCase): + + def test_sanity(self): + self.assertRegexpMatches( + ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") + + def test_font_with_name(self): + ImageFont.truetype(font_path, font_size) + self._render(font_path) + self._clean() + + def _font_as_bytes(self): + with open(font_path, 'rb') as f: + font_bytes = BytesIO(f.read()) + return font_bytes + + def test_font_with_filelike(self): + ImageFont.truetype(self._font_as_bytes(), font_size) + self._render(self._font_as_bytes()) + # Usage note: making two fonts from the same buffer fails. + # shared_bytes = self._font_as_bytes() + # self._render(shared_bytes) + # self.assertRaises(Exception, lambda: _render(shared_bytes)) + self._clean() + + def test_font_with_open_file(self): + with open(font_path, 'rb') as f: + self._render(f) + self._clean() + + def test_font_old_parameters(self): + self.assert_warning( + DeprecationWarning, + lambda: ImageFont.truetype(filename=font_path, size=font_size)) + + def _render(self, font): + txt = "Hello World!" + ttf = ImageFont.truetype(font, font_size) + w, h = ttf.getsize(txt) + img = Image.new("RGB", (256, 64), "white") + d = ImageDraw.Draw(img) + d.text((10, 10), txt, font=ttf, fill='black') + + img.save('font.png') + return img + + def _clean(self): + os.unlink('font.png') + + def test_render_equal(self): + img_path = self._render(font_path) + with open(font_path, 'rb') as f: + font_filelike = BytesIO(f.read()) + img_filelike = self._render(font_filelike) + + self.assert_image_equal(img_path, img_filelike) + self._clean() + + def test_render_multiline(self): + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + ttf = ImageFont.truetype(font_path, font_size) + line_spacing = draw.textsize('A', font=ttf)[1] + 8 + lines = ['hey you', 'you are awesome', 'this looks awkward'] + y = 0 + for line in lines: + draw.text((0, y), line, font=ttf) + y += line_spacing + + target = 'Tests/images/multiline_text.png' + target_img = Image.open(target) + + # some versions of freetype have different horizontal spacing. + # setting a tight epsilon, I'm showing the original test failure + # at epsilon = ~38. + self.assert_image_similar(im, target_img, .5) + + def test_rotated_transposed_font(self): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = Image.ROTATE_90 + transposed_font = ImageFont.TransposedFont( + font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # 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[1], box_size_b[0]) + + def test_unrotated_transposed_font(self): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = None + transposed_font = ImageFont.TransposedFont( + font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check boxes a and b are same size + self.assertEqual(box_size_a, box_size_b) + except ImportError: - skip() - -from PIL import ImageDraw - -font_path = "Tests/fonts/FreeMono.ttf" -font_size=20 - -def test_sanity(): - assert_match(ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") - -def test_font_with_name(): - assert_no_exception(lambda: ImageFont.truetype(font_path, font_size)) - assert_no_exception(lambda: _render(font_path)) - _clean() - -def _font_as_bytes(): - with open(font_path, 'rb') as f: - font_bytes = BytesIO(f.read()) - return font_bytes - -def test_font_with_filelike(): - assert_no_exception(lambda: ImageFont.truetype(_font_as_bytes(), font_size)) - assert_no_exception(lambda: _render(_font_as_bytes())) - # Usage note: making two fonts from the same buffer fails. - #shared_bytes = _font_as_bytes() - #assert_no_exception(lambda: _render(shared_bytes)) - #assert_exception(Exception, lambda: _render(shared_bytes)) - _clean() - -def test_font_with_open_file(): - with open(font_path, 'rb') as f: - assert_no_exception(lambda: _render(f)) - _clean() - -def test_font_old_parameters(): - assert_warning(DeprecationWarning, lambda: ImageFont.truetype(filename=font_path, size=font_size)) - -def _render(font): - txt = "Hello World!" - ttf = ImageFont.truetype(font, font_size) - w, h = ttf.getsize(txt) - img = Image.new("RGB", (256, 64), "white") - d = ImageDraw.Draw(img) - d.text((10, 10), txt, font=ttf, fill='black') - - img.save('font.png') - return img - -def _clean(): - os.unlink('font.png') - -def test_render_equal(): - img_path = _render(font_path) - with open(font_path, 'rb') as f: - font_filelike = BytesIO(f.read()) - img_filelike = _render(font_filelike) - - assert_image_equal(img_path, img_filelike) - _clean() + class TestImageFont(PillowTestCase): + def test_skip(self): + self.skipTest("ImportError") -def test_render_multiline(): - im = Image.new(mode='RGB', size=(300,100)) - draw = ImageDraw.Draw(im) - ttf = ImageFont.truetype(font_path, font_size) - line_spacing = draw.textsize('A', font=ttf)[1] + 8 - lines = ['hey you', 'you are awesome', 'this looks awkward'] - y = 0 - for line in lines: - draw.text((0, y), line, font=ttf) - y += line_spacing - - - target = 'Tests/images/multiline_text.png' - target_img = Image.open(target) - - # some versions of freetype have different horizontal spacing. - # setting a tight epsilon, I'm showing the original test failure - # at epsilon = ~38. - assert_image_similar(im, target_img,.5) - - -def test_rotated_transposed_font(): - img_grey = Image.new("L", (100, 100)) - draw = ImageDraw.Draw(img_grey) - word = "testing" - font = ImageFont.truetype(font_path, font_size) - - orientation = Image.ROTATE_90 - transposed_font = ImageFont.TransposedFont(font, orientation=orientation) - - # Original font - draw.setfont(font) - box_size_a = draw.textsize(word) - - # Rotated font - draw.setfont(transposed_font) - box_size_b = draw.textsize(word) - - # Check (w,h) of box a is (h,w) of box b - assert_equal(box_size_a[0], box_size_b[1]) - assert_equal(box_size_a[1], box_size_b[0]) - - -def test_unrotated_transposed_font(): - img_grey = Image.new("L", (100, 100)) - draw = ImageDraw.Draw(img_grey) - word = "testing" - font = ImageFont.truetype(font_path, font_size) - - orientation = None - transposed_font = ImageFont.TransposedFont(font, orientation=orientation) - - # Original font - draw.setfont(font) - box_size_a = draw.textsize(word) - - # Rotated font - draw.setfont(transposed_font) - box_size_b = draw.textsize(word) - - # Check boxes a and b are same size - assert_equal(box_size_a, box_size_b) - +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 67ff71960..a6c50fb31 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -1,13 +1,25 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule -from PIL import Image try: from PIL import ImageGrab -except ImportError as v: - skip(v) -def test_grab(): - im = ImageGrab.grab() - assert_image(im, im.mode, im.size) + class TestImageCopy(PillowTestCase): + + def test_grab(self): + im = ImageGrab.grab() + self.assert_image(im, im.mode, im.size) + + def test_grab2(self): + im = ImageGrab.grab() + self.assert_image(im, im.mode, im.size) + +except ImportError: + class TestImageCopy(PillowTestCase): + def test_skip(self): + self.skipTest("ImportError") +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index eaeb711ba..35d75dbbd 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -1,14 +1,15 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image from PIL import ImageMath + def pixel(im): if hasattr(im, "im"): return "%s %r" % (im.mode, im.getpixel((0, 0))) else: if isinstance(im, type(0)): - return int(im) # hack to deal with booleans + return int(im) # hack to deal with booleans print(im) A = Image.new("L", (1, 1), 1) @@ -18,45 +19,60 @@ I = Image.new("I", (1, 1), 4) images = {"A": A, "B": B, "F": F, "I": I} -def test_sanity(): - assert_equal(ImageMath.eval("1"), 1) - assert_equal(ImageMath.eval("1+A", A=2), 3) - assert_equal(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") - assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") - assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") - assert_equal(pixel(ImageMath.eval("int(float(A)+B)", images)), "I 3") -def test_ops(): +class TestImageMath(PillowTestCase): - assert_equal(pixel(ImageMath.eval("-A", images)), "I -1") - assert_equal(pixel(ImageMath.eval("+B", images)), "L 2") + def test_sanity(self): + self.assertEqual(ImageMath.eval("1"), 1) + self.assertEqual(ImageMath.eval("1+A", A=2), 3) + self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") + self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") + self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + self.assertEqual(pixel( + ImageMath.eval("int(float(A)+B)", images)), "I 3") - assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") - assert_equal(pixel(ImageMath.eval("A-B", images)), "I -1") - assert_equal(pixel(ImageMath.eval("A*B", images)), "I 2") - assert_equal(pixel(ImageMath.eval("A/B", images)), "I 0") - assert_equal(pixel(ImageMath.eval("B**2", images)), "I 4") - assert_equal(pixel(ImageMath.eval("B**33", images)), "I 2147483647") + def test_ops(self): - assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") - assert_equal(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") - assert_equal(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") - assert_equal(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") - assert_equal(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") - assert_equal(pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0") + self.assertEqual(pixel(ImageMath.eval("-A", images)), "I -1") + self.assertEqual(pixel(ImageMath.eval("+B", images)), "L 2") -def test_logical(): - assert_equal(pixel(ImageMath.eval("not A", images)), 0) - assert_equal(pixel(ImageMath.eval("A and B", images)), "L 2") - assert_equal(pixel(ImageMath.eval("A or B", images)), "L 1") + self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") + self.assertEqual(pixel(ImageMath.eval("A-B", images)), "I -1") + self.assertEqual(pixel(ImageMath.eval("A*B", images)), "I 2") + self.assertEqual(pixel(ImageMath.eval("A/B", images)), "I 0") + self.assertEqual(pixel(ImageMath.eval("B**2", images)), "I 4") + self.assertEqual(pixel( + ImageMath.eval("B**33", images)), "I 2147483647") -def test_convert(): - assert_equal(pixel(ImageMath.eval("convert(A+B, 'L')", images)), "L 3") - assert_equal(pixel(ImageMath.eval("convert(A+B, '1')", images)), "1 0") - assert_equal(pixel(ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") + self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + self.assertEqual(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") + self.assertEqual(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") + self.assertEqual(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") + self.assertEqual(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") + self.assertEqual(pixel( + ImageMath.eval("float(B)**33", images)), "F 8589934592.0") -def test_compare(): - assert_equal(pixel(ImageMath.eval("min(A, B)", images)), "I 1") - assert_equal(pixel(ImageMath.eval("max(A, B)", images)), "I 2") - assert_equal(pixel(ImageMath.eval("A == 1", images)), "I 1") - assert_equal(pixel(ImageMath.eval("A == 2", images)), "I 0") + def test_logical(self): + self.assertEqual(pixel(ImageMath.eval("not A", images)), 0) + self.assertEqual(pixel(ImageMath.eval("A and B", images)), "L 2") + self.assertEqual(pixel(ImageMath.eval("A or B", images)), "L 1") + + def test_convert(self): + self.assertEqual(pixel( + ImageMath.eval("convert(A+B, 'L')", images)), "L 3") + self.assertEqual(pixel( + ImageMath.eval("convert(A+B, '1')", images)), "1 0") + self.assertEqual(pixel( + ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") + + def test_compare(self): + self.assertEqual(pixel(ImageMath.eval("min(A, B)", images)), "I 1") + self.assertEqual(pixel(ImageMath.eval("max(A, B)", images)), "I 2") + self.assertEqual(pixel(ImageMath.eval("A == 1", images)), "I 1") + self.assertEqual(pixel(ImageMath.eval("A == 2", images)), "I 0") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagemode.py b/Tests/test_imagemode.py index 54b04435f..7febc697e 100644 --- a/Tests/test_imagemode.py +++ b/Tests/test_imagemode.py @@ -1,23 +1,32 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule -from PIL import Image from PIL import ImageMode -ImageMode.getmode("1") -ImageMode.getmode("L") -ImageMode.getmode("P") -ImageMode.getmode("RGB") -ImageMode.getmode("I") -ImageMode.getmode("F") -m = ImageMode.getmode("1") -assert_equal(m.mode, "1") -assert_equal(m.bands, ("1",)) -assert_equal(m.basemode, "L") -assert_equal(m.basetype, "L") +class TestImageMode(PillowTestCase): -m = ImageMode.getmode("RGB") -assert_equal(m.mode, "RGB") -assert_equal(m.bands, ("R", "G", "B")) -assert_equal(m.basemode, "RGB") -assert_equal(m.basetype, "L") + def test_sanity(self): + ImageMode.getmode("1") + ImageMode.getmode("L") + ImageMode.getmode("P") + ImageMode.getmode("RGB") + ImageMode.getmode("I") + ImageMode.getmode("F") + + m = ImageMode.getmode("1") + self.assertEqual(m.mode, "1") + self.assertEqual(m.bands, ("1",)) + self.assertEqual(m.basemode, "L") + self.assertEqual(m.basetype, "L") + + m = ImageMode.getmode("RGB") + self.assertEqual(m.mode, "RGB") + self.assertEqual(m.bands, ("R", "G", "B")) + self.assertEqual(m.basemode, "RGB") + self.assertEqual(m.basetype, "L") + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 8ed5ccefa..299a7c618 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,81 +1,85 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena -from PIL import Image from PIL import ImageOps -class Deformer: - def getmesh(self, im): - x, y = im.size - return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] -deformer = Deformer() +class TestImageOps(PillowTestCase): -def test_sanity(): + class Deformer: + def getmesh(self, im): + x, y = im.size + return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] - ImageOps.autocontrast(lena("L")) - ImageOps.autocontrast(lena("RGB")) + deformer = Deformer() - ImageOps.autocontrast(lena("L"), cutoff=10) - ImageOps.autocontrast(lena("L"), ignore=[0, 255]) + def test_sanity(self): - ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) - ImageOps.colorize(lena("L"), "black", "white") + ImageOps.autocontrast(lena("L")) + ImageOps.autocontrast(lena("RGB")) - ImageOps.crop(lena("L"), 1) - ImageOps.crop(lena("RGB"), 1) + ImageOps.autocontrast(lena("L"), cutoff=10) + ImageOps.autocontrast(lena("L"), ignore=[0, 255]) - ImageOps.deform(lena("L"), deformer) - ImageOps.deform(lena("RGB"), deformer) + ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) + ImageOps.colorize(lena("L"), "black", "white") - ImageOps.equalize(lena("L")) - ImageOps.equalize(lena("RGB")) + ImageOps.crop(lena("L"), 1) + ImageOps.crop(lena("RGB"), 1) - ImageOps.expand(lena("L"), 1) - ImageOps.expand(lena("RGB"), 1) - ImageOps.expand(lena("L"), 2, "blue") - ImageOps.expand(lena("RGB"), 2, "blue") + ImageOps.deform(lena("L"), self.deformer) + ImageOps.deform(lena("RGB"), self.deformer) - ImageOps.fit(lena("L"), (128, 128)) - ImageOps.fit(lena("RGB"), (128, 128)) + ImageOps.equalize(lena("L")) + ImageOps.equalize(lena("RGB")) - ImageOps.flip(lena("L")) - ImageOps.flip(lena("RGB")) + ImageOps.expand(lena("L"), 1) + ImageOps.expand(lena("RGB"), 1) + ImageOps.expand(lena("L"), 2, "blue") + ImageOps.expand(lena("RGB"), 2, "blue") - ImageOps.grayscale(lena("L")) - ImageOps.grayscale(lena("RGB")) + ImageOps.fit(lena("L"), (128, 128)) + ImageOps.fit(lena("RGB"), (128, 128)) - ImageOps.invert(lena("L")) - ImageOps.invert(lena("RGB")) + ImageOps.flip(lena("L")) + ImageOps.flip(lena("RGB")) - ImageOps.mirror(lena("L")) - ImageOps.mirror(lena("RGB")) + ImageOps.grayscale(lena("L")) + ImageOps.grayscale(lena("RGB")) - ImageOps.posterize(lena("L"), 4) - ImageOps.posterize(lena("RGB"), 4) + ImageOps.invert(lena("L")) + ImageOps.invert(lena("RGB")) - ImageOps.solarize(lena("L")) - ImageOps.solarize(lena("RGB")) + ImageOps.mirror(lena("L")) + ImageOps.mirror(lena("RGB")) - success() + ImageOps.posterize(lena("L"), 4) + ImageOps.posterize(lena("RGB"), 4) -def test_1pxfit(): - # Division by zero in equalize if image is 1 pixel high - newimg = ImageOps.fit(lena("RGB").resize((1,1)), (35,35)) - assert_equal(newimg.size,(35,35)) - - newimg = ImageOps.fit(lena("RGB").resize((1,100)), (35,35)) - assert_equal(newimg.size,(35,35)) + ImageOps.solarize(lena("L")) + ImageOps.solarize(lena("RGB")) - newimg = ImageOps.fit(lena("RGB").resize((100,1)), (35,35)) - assert_equal(newimg.size,(35,35)) + def test_1pxfit(self): + # Division by zero in equalize if image is 1 pixel high + newimg = ImageOps.fit(lena("RGB").resize((1, 1)), (35, 35)) + self.assertEqual(newimg.size, (35, 35)) -def test_pil163(): - # Division by zero in equalize if < 255 pixels in image (@PIL163) + newimg = ImageOps.fit(lena("RGB").resize((1, 100)), (35, 35)) + self.assertEqual(newimg.size, (35, 35)) - i = lena("RGB").resize((15, 16)) + newimg = ImageOps.fit(lena("RGB").resize((100, 1)), (35, 35)) + self.assertEqual(newimg.size, (35, 35)) - ImageOps.equalize(i.convert("L")) - ImageOps.equalize(i.convert("P")) - ImageOps.equalize(i.convert("RGB")) + def test_pil163(self): + # Division by zero in equalize if < 255 pixels in image (@PIL163) - success() + i = lena("RGB").resize((15, 16)) + + ImageOps.equalize(i.convert("L")) + ImageOps.equalize(i.convert("P")) + ImageOps.equalize(i.convert("RGB")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index 83a93b8e0..2f81eebce 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -1,4 +1,4 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image from PIL import ImageOps @@ -6,50 +6,59 @@ from PIL import ImageFilter im = Image.open("Images/lena.ppm") -def test_ops_api(): - i = ImageOps.gaussian_blur(im, 2.0) - assert_equal(i.mode, "RGB") - assert_equal(i.size, (128, 128)) - # i.save("blur.bmp") +class TestImageOpsUsm(PillowTestCase): - i = ImageOps.usm(im, 2.0, 125, 8) - assert_equal(i.mode, "RGB") - assert_equal(i.size, (128, 128)) - # i.save("usm.bmp") + def test_ops_api(self): -def test_filter_api(): + i = ImageOps.gaussian_blur(im, 2.0) + self.assertEqual(i.mode, "RGB") + self.assertEqual(i.size, (128, 128)) + # i.save("blur.bmp") - filter = ImageFilter.GaussianBlur(2.0) - i = im.filter(filter) - assert_equal(i.mode, "RGB") - assert_equal(i.size, (128, 128)) + i = ImageOps.usm(im, 2.0, 125, 8) + self.assertEqual(i.mode, "RGB") + self.assertEqual(i.size, (128, 128)) + # i.save("usm.bmp") - filter = ImageFilter.UnsharpMask(2.0, 125, 8) - i = im.filter(filter) - assert_equal(i.mode, "RGB") - assert_equal(i.size, (128, 128)) + def test_filter_api(self): -def test_usm(): + filter = ImageFilter.GaussianBlur(2.0) + i = im.filter(filter) + self.assertEqual(i.mode, "RGB") + self.assertEqual(i.size, (128, 128)) - usm = ImageOps.unsharp_mask - assert_exception(ValueError, lambda: usm(im.convert("1"))) - assert_no_exception(lambda: usm(im.convert("L"))) - assert_exception(ValueError, lambda: usm(im.convert("I"))) - assert_exception(ValueError, lambda: usm(im.convert("F"))) - assert_no_exception(lambda: usm(im.convert("RGB"))) - assert_no_exception(lambda: usm(im.convert("RGBA"))) - assert_no_exception(lambda: usm(im.convert("CMYK"))) - assert_exception(ValueError, lambda: usm(im.convert("YCbCr"))) + filter = ImageFilter.UnsharpMask(2.0, 125, 8) + i = im.filter(filter) + self.assertEqual(i.mode, "RGB") + self.assertEqual(i.size, (128, 128)) -def test_blur(): + def test_usm(self): - blur = ImageOps.gaussian_blur - assert_exception(ValueError, lambda: blur(im.convert("1"))) - assert_no_exception(lambda: blur(im.convert("L"))) - assert_exception(ValueError, lambda: blur(im.convert("I"))) - assert_exception(ValueError, lambda: blur(im.convert("F"))) - assert_no_exception(lambda: blur(im.convert("RGB"))) - assert_no_exception(lambda: blur(im.convert("RGBA"))) - assert_no_exception(lambda: blur(im.convert("CMYK"))) - assert_exception(ValueError, lambda: blur(im.convert("YCbCr"))) + usm = ImageOps.unsharp_mask + self.assertRaises(ValueError, lambda: usm(im.convert("1"))) + usm(im.convert("L")) + self.assertRaises(ValueError, lambda: usm(im.convert("I"))) + self.assertRaises(ValueError, lambda: usm(im.convert("F"))) + usm(im.convert("RGB")) + usm(im.convert("RGBA")) + usm(im.convert("CMYK")) + self.assertRaises(ValueError, lambda: usm(im.convert("YCbCr"))) + + def test_blur(self): + + blur = ImageOps.gaussian_blur + self.assertRaises(ValueError, lambda: blur(im.convert("1"))) + blur(im.convert("L")) + self.assertRaises(ValueError, lambda: blur(im.convert("I"))) + self.assertRaises(ValueError, lambda: blur(im.convert("F"))) + blur(im.convert("RGB")) + blur(im.convert("RGBA")) + blur(im.convert("CMYK")) + self.assertRaises(ValueError, lambda: blur(im.convert("YCbCr"))) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index a22addda9..4d7e067f4 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -1,44 +1,50 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule -from PIL import Image from PIL import ImagePalette ImagePalette = ImagePalette.ImagePalette -def test_sanity(): - assert_no_exception(lambda: ImagePalette("RGB", list(range(256))*3)) - assert_exception(ValueError, lambda: ImagePalette("RGB", list(range(256))*2)) +class TestImagePalette(PillowTestCase): -def test_getcolor(): + def test_sanity(self): - palette = ImagePalette() + ImagePalette("RGB", list(range(256))*3) + self.assertRaises( + ValueError, lambda: ImagePalette("RGB", list(range(256))*2)) - map = {} - for i in range(256): - map[palette.getcolor((i, i, i))] = i + def test_getcolor(self): - assert_equal(len(map), 256) - assert_exception(ValueError, lambda: palette.getcolor((1, 2, 3))) + palette = ImagePalette() -def test_file(): + map = {} + for i in range(256): + map[palette.getcolor((i, i, i))] = i - palette = ImagePalette() + self.assertEqual(len(map), 256) + self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3))) - file = tempfile("temp.lut") + def test_file(self): - palette.save(file) + palette = ImagePalette() - from PIL.ImagePalette import load, raw + file = self.tempfile("temp.lut") - p = load(file) + palette.save(file) - # load returns raw palette information - assert_equal(len(p[0]), 768) - assert_equal(p[1], "RGB") + from PIL.ImagePalette import load, raw - p = raw(p[1], p[0]) - assert_true(isinstance(p, ImagePalette)) + p = load(file) + + # load returns raw palette information + self.assertEqual(len(p[0]), 768) + self.assertEqual(p[1], "RGB") + + p = raw(p[1], p[0]) + self.assertIsInstance(p, ImagePalette) +if __name__ == '__main__': + unittest.main() +# End of file diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py index 9e9e63c3a..c293e4225 100644 --- a/Tests/test_imagepath.py +++ b/Tests/test_imagepath.py @@ -1,54 +1,68 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule -from PIL import Image from PIL import ImagePath import array -def test_path(): - p = ImagePath.Path(list(range(10))) +class TestImagePath(PillowTestCase): - # sequence interface - assert_equal(len(p), 5) - assert_equal(p[0], (0.0, 1.0)) - assert_equal(p[-1], (8.0, 9.0)) - assert_equal(list(p[:1]), [(0.0, 1.0)]) - assert_equal(list(p), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + def test_path(self): - # method sanity check - assert_equal(p.tolist(), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) - assert_equal(p.tolist(1), [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]) + p = ImagePath.Path(list(range(10))) - assert_equal(p.getbbox(), (0.0, 1.0, 8.0, 9.0)) + # sequence interface + self.assertEqual(len(p), 5) + self.assertEqual(p[0], (0.0, 1.0)) + self.assertEqual(p[-1], (8.0, 9.0)) + self.assertEqual(list(p[:1]), [(0.0, 1.0)]) + self.assertEqual( + list(p), + [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) - assert_equal(p.compact(5), 2) - assert_equal(list(p), [(0.0, 1.0), (4.0, 5.0), (8.0, 9.0)]) + # method sanity check + self.assertEqual( + p.tolist(), + [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + self.assertEqual( + p.tolist(1), + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]) - p.transform((1,0,1,0,1,1)) - assert_equal(list(p), [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]) + self.assertEqual(p.getbbox(), (0.0, 1.0, 8.0, 9.0)) - # alternative constructors - p = ImagePath.Path([0, 1]) - assert_equal(list(p), [(0.0, 1.0)]) - p = ImagePath.Path([0.0, 1.0]) - assert_equal(list(p), [(0.0, 1.0)]) - p = ImagePath.Path([0, 1]) - assert_equal(list(p), [(0.0, 1.0)]) - p = ImagePath.Path([(0, 1)]) - assert_equal(list(p), [(0.0, 1.0)]) - p = ImagePath.Path(p) - assert_equal(list(p), [(0.0, 1.0)]) - p = ImagePath.Path(p.tolist(0)) - assert_equal(list(p), [(0.0, 1.0)]) - p = ImagePath.Path(p.tolist(1)) - assert_equal(list(p), [(0.0, 1.0)]) - p = ImagePath.Path(array.array("f", [0, 1])) - assert_equal(list(p), [(0.0, 1.0)]) + self.assertEqual(p.compact(5), 2) + self.assertEqual(list(p), [(0.0, 1.0), (4.0, 5.0), (8.0, 9.0)]) - arr = array.array("f", [0, 1]) - if hasattr(arr, 'tobytes'): - p = ImagePath.Path(arr.tobytes()) - else: - p = ImagePath.Path(arr.tostring()) - assert_equal(list(p), [(0.0, 1.0)]) + p.transform((1, 0, 1, 0, 1, 1)) + self.assertEqual(list(p), [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]) + + # alternative constructors + p = ImagePath.Path([0, 1]) + self.assertEqual(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([0.0, 1.0]) + self.assertEqual(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([0, 1]) + self.assertEqual(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([(0, 1)]) + self.assertEqual(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p) + self.assertEqual(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p.tolist(0)) + self.assertEqual(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p.tolist(1)) + self.assertEqual(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(array.array("f", [0, 1])) + self.assertEqual(list(p), [(0.0, 1.0)]) + + arr = array.array("f", [0, 1]) + if hasattr(arr, 'tobytes'): + p = ImagePath.Path(arr.tobytes()) + else: + p = ImagePath.Path(arr.tostring()) + self.assertEqual(list(p), [(0.0, 1.0)]) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index 73d1f4b1c..549fc7fd6 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -1,37 +1,53 @@ -from tester import * - -from PIL import Image +from helper import unittest, PillowTestCase, tearDownModule, lena try: + from PIL import ImageQt from PyQt5.QtGui import QImage, qRgb, qRgba except: try: from PyQt4.QtGui import QImage, qRgb, qRgba except: - skip('PyQT4 or 5 not installed') - -from PIL import ImageQt - -def test_rgb(): - # from https://qt-project.org/doc/qt-4.8/qcolor.html - # typedef QRgb - # An ARGB quadruplet on the format #AARRGGBB, equivalent to an unsigned int. - - assert_equal(qRgb(0,0,0), qRgba(0,0,0,255)) - - def checkrgb(r,g,b): - val = ImageQt.rgb(r,g,b) - val = val % 2**24 # drop the alpha - assert_equal(val >> 16, r) - assert_equal(((val >> 8 ) % 2**8), g) - assert_equal(val % 2**8, b) - - checkrgb(0,0,0) - checkrgb(255,0,0) - checkrgb(0,255,0) - checkrgb(0,0,255) + # Will be skipped in setUp + pass -def test_image(): - for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): - assert_no_exception(lambda: ImageQt.ImageQt(lena(mode))) +class TestImageQt(PillowTestCase): + + def setUp(self): + try: + from PyQt5.QtGui import QImage, qRgb, qRgba + except: + try: + from PyQt4.QtGui import QImage, qRgb, qRgba + except: + self.skipTest('PyQt4 or 5 not installed') + + def test_rgb(self): + # from https://qt-project.org/doc/qt-4.8/qcolor.html + # typedef QRgb + # An ARGB quadruplet on the format #AARRGGBB, + # equivalent to an unsigned int. + + self.assertEqual(qRgb(0, 0, 0), qRgba(0, 0, 0, 255)) + + def checkrgb(r, g, b): + val = ImageQt.rgb(r, g, b) + val = val % 2**24 # drop the alpha + self.assertEqual(val >> 16, r) + self.assertEqual(((val >> 8) % 2**8), g) + self.assertEqual(val % 2**8, b) + + checkrgb(0, 0, 0) + checkrgb(255, 0, 0) + checkrgb(0, 255, 0) + checkrgb(0, 0, 255) + + def test_image(self): + for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): + ImageQt.ImageQt(lena(mode)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 3329b1a05..9001502ac 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -1,22 +1,29 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena -from PIL import Image from PIL import ImageSequence -def test_sanity(): - file = tempfile("temp.im") +class TestImageSequence(PillowTestCase): - im = lena("RGB") - im.save(file) + def test_sanity(self): - seq = ImageSequence.Iterator(im) + file = self.tempfile("temp.im") - index = 0 - for frame in seq: - assert_image_equal(im, frame) - assert_equal(im.tell(), index) - index = index + 1 + im = lena("RGB") + im.save(file) - assert_equal(index, 1) + seq = ImageSequence.Iterator(im) + index = 0 + for frame in seq: + self.assert_image_equal(im, frame) + self.assertEqual(im.tell(), index) + index = index + 1 + + self.assertEqual(index, 1) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 99ec005c8..08b3ff183 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -1,6 +1,18 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image from PIL import ImageShow -success() + +class TestImageShow(PillowTestCase): + + def test_sanity(self): + dir(Image) + dir(ImageShow) + pass + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py index 02a461e22..7eded56cf 100644 --- a/Tests/test_imagestat.py +++ b/Tests/test_imagestat.py @@ -1,52 +1,63 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image from PIL import ImageStat -def test_sanity(): - im = lena() +class TestImageStat(PillowTestCase): - st = ImageStat.Stat(im) - st = ImageStat.Stat(im.histogram()) - st = ImageStat.Stat(im, Image.new("1", im.size, 1)) + def test_sanity(self): - assert_no_exception(lambda: st.extrema) - assert_no_exception(lambda: st.sum) - assert_no_exception(lambda: st.mean) - assert_no_exception(lambda: st.median) - assert_no_exception(lambda: st.rms) - assert_no_exception(lambda: st.sum2) - assert_no_exception(lambda: st.var) - assert_no_exception(lambda: st.stddev) - assert_exception(AttributeError, lambda: st.spam) + im = lena() - assert_exception(TypeError, lambda: ImageStat.Stat(1)) + st = ImageStat.Stat(im) + st = ImageStat.Stat(im.histogram()) + st = ImageStat.Stat(im, Image.new("1", im.size, 1)) -def test_lena(): + # Check these run. Exceptions will cause failures. + st.extrema + st.sum + st.mean + st.median + st.rms + st.sum2 + st.var + st.stddev - im = lena() + self.assertRaises(AttributeError, lambda: st.spam) - st = ImageStat.Stat(im) + self.assertRaises(TypeError, lambda: ImageStat.Stat(1)) - # verify a few values - assert_equal(st.extrema[0], (61, 255)) - assert_equal(st.median[0], 197) - assert_equal(st.sum[0], 2954416) - assert_equal(st.sum[1], 2027250) - assert_equal(st.sum[2], 1727331) + def test_lena(self): -def test_constant(): + im = lena() - im = Image.new("L", (128, 128), 128) + st = ImageStat.Stat(im) - st = ImageStat.Stat(im) + # verify a few values + self.assertEqual(st.extrema[0], (61, 255)) + self.assertEqual(st.median[0], 197) + self.assertEqual(st.sum[0], 2954416) + self.assertEqual(st.sum[1], 2027250) + self.assertEqual(st.sum[2], 1727331) - assert_equal(st.extrema[0], (128, 128)) - assert_equal(st.sum[0], 128**3) - assert_equal(st.sum2[0], 128**4) - assert_equal(st.mean[0], 128) - assert_equal(st.median[0], 128) - assert_equal(st.rms[0], 128) - assert_equal(st.var[0], 0) - assert_equal(st.stddev[0], 0) + def test_constant(self): + + im = Image.new("L", (128, 128), 128) + + st = ImageStat.Stat(im) + + self.assertEqual(st.extrema[0], (128, 128)) + self.assertEqual(st.sum[0], 128**3) + self.assertEqual(st.sum2[0], 128**4) + self.assertEqual(st.mean[0], 128) + self.assertEqual(st.median[0], 128) + self.assertEqual(st.rms[0], 128) + self.assertEqual(st.var[0], 0) + self.assertEqual(st.stddev[0], 0) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index b30971e8f..b868096b2 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -1,9 +1,17 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule -from PIL import Image -try: - from PIL import ImageTk -except (OSError, ImportError) as v: - skip(v) -success() +class TestImageTk(PillowTestCase): + + def test_import(self): + try: + from PIL import ImageTk + dir(ImageTk) + except (OSError, ImportError) as v: + self.skipTest(v) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagetransform.py b/Tests/test_imagetransform.py index 884e6bb1c..dfffafe54 100644 --- a/Tests/test_imagetransform.py +++ b/Tests/test_imagetransform.py @@ -1,18 +1,27 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image from PIL import ImageTransform -im = Image.new("L", (100, 100)) -seq = tuple(range(10)) +class TestImageTransform(PillowTestCase): -def test_sanity(): - transform = ImageTransform.AffineTransform(seq[:6]) - assert_no_exception(lambda: im.transform((100, 100), transform)) - transform = ImageTransform.ExtentTransform(seq[:4]) - assert_no_exception(lambda: im.transform((100, 100), transform)) - transform = ImageTransform.QuadTransform(seq[:8]) - assert_no_exception(lambda: im.transform((100, 100), transform)) - transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) - assert_no_exception(lambda: im.transform((100, 100), transform)) + def test_sanity(self): + im = Image.new("L", (100, 100)) + + seq = tuple(range(10)) + + transform = ImageTransform.AffineTransform(seq[:6]) + im.transform((100, 100), transform) + transform = ImageTransform.ExtentTransform(seq[:4]) + im.transform((100, 100), transform) + transform = ImageTransform.QuadTransform(seq[:8]) + im.transform((100, 100), transform) + transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) + im.transform((100, 100), transform) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 268a75b6f..f22babbb3 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -1,6 +1,18 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image from PIL import ImageWin -success() + +class TestImageWin(PillowTestCase): + + def test_sanity(self): + dir(Image) + dir(ImageWin) + pass + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py index 93aa694d8..c7ea4c701 100644 --- a/Tests/test_lib_image.py +++ b/Tests/test_lib_image.py @@ -1,30 +1,39 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule from PIL import Image -def test_setmode(): - im = Image.new("L", (1, 1), 255) - im.im.setmode("1") - assert_equal(im.im.getpixel((0, 0)), 255) - im.im.setmode("L") - assert_equal(im.im.getpixel((0, 0)), 255) +class TestSanity(PillowTestCase): - im = Image.new("1", (1, 1), 1) - im.im.setmode("L") - assert_equal(im.im.getpixel((0, 0)), 255) - im.im.setmode("1") - assert_equal(im.im.getpixel((0, 0)), 255) + def test_setmode(self): - im = Image.new("RGB", (1, 1), (1, 2, 3)) - im.im.setmode("RGB") - assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) - im.im.setmode("RGBA") - assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) - im.im.setmode("RGBX") - assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) - im.im.setmode("RGB") - assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + im = Image.new("L", (1, 1), 255) + im.im.setmode("1") + self.assertEqual(im.im.getpixel((0, 0)), 255) + im.im.setmode("L") + self.assertEqual(im.im.getpixel((0, 0)), 255) - assert_exception(ValueError, lambda: im.im.setmode("L")) - assert_exception(ValueError, lambda: im.im.setmode("RGBABCDE")) + im = Image.new("1", (1, 1), 1) + im.im.setmode("L") + self.assertEqual(im.im.getpixel((0, 0)), 255) + im.im.setmode("1") + self.assertEqual(im.im.getpixel((0, 0)), 255) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.im.setmode("RGB") + self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3)) + im.im.setmode("RGBA") + self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGBX") + self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGB") + self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3)) + + self.assertRaises(ValueError, lambda: im.im.setmode("L")) + self.assertRaises(ValueError, lambda: im.im.setmode("RGBABCDE")) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 7675348b3..c8ed39c40 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -1,138 +1,147 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, py3 from PIL import Image -def pack(): - pass # not yet -def test_pack(): +class TestLibPack(PillowTestCase): - def pack(mode, rawmode): - if len(mode) == 1: - im = Image.new(mode, (1, 1), 1) - else: - im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) + def pack(self): + pass # not yet - if py3: - return list(im.tobytes("raw", rawmode)) - else: - return [ord(c) for c in im.tobytes("raw", rawmode)] + def test_pack(self): - order = 1 if Image._ENDIAN == '<' else -1 + def pack(mode, rawmode): + if len(mode) == 1: + im = Image.new(mode, (1, 1), 1) + else: + im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) - assert_equal(pack("1", "1"), [128]) - assert_equal(pack("1", "1;I"), [0]) - assert_equal(pack("1", "1;R"), [1]) - assert_equal(pack("1", "1;IR"), [0]) + if py3: + return list(im.tobytes("raw", rawmode)) + else: + return [ord(c) for c in im.tobytes("raw", rawmode)] - assert_equal(pack("L", "L"), [1]) + order = 1 if Image._ENDIAN == '<' else -1 - assert_equal(pack("I", "I"), [1, 0, 0, 0][::order]) + self.assertEqual(pack("1", "1"), [128]) + self.assertEqual(pack("1", "1;I"), [0]) + self.assertEqual(pack("1", "1;R"), [1]) + self.assertEqual(pack("1", "1;IR"), [0]) - assert_equal(pack("F", "F"), [0, 0, 128, 63][::order]) + self.assertEqual(pack("L", "L"), [1]) - assert_equal(pack("LA", "LA"), [1, 2]) + self.assertEqual(pack("I", "I"), [1, 0, 0, 0][::order]) - assert_equal(pack("RGB", "RGB"), [1, 2, 3]) - assert_equal(pack("RGB", "RGB;L"), [1, 2, 3]) - assert_equal(pack("RGB", "BGR"), [3, 2, 1]) - assert_equal(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? - assert_equal(pack("RGB", "BGRX"), [3, 2, 1, 0]) - assert_equal(pack("RGB", "XRGB"), [0, 1, 2, 3]) - assert_equal(pack("RGB", "XBGR"), [0, 3, 2, 1]) + self.assertEqual(pack("F", "F"), [0, 0, 128, 63][::order]) - assert_equal(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? + self.assertEqual(pack("LA", "LA"), [1, 2]) - assert_equal(pack("RGBA", "RGBA"), [1, 2, 3, 4]) + self.assertEqual(pack("RGB", "RGB"), [1, 2, 3]) + self.assertEqual(pack("RGB", "RGB;L"), [1, 2, 3]) + self.assertEqual(pack("RGB", "BGR"), [3, 2, 1]) + self.assertEqual(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? + self.assertEqual(pack("RGB", "BGRX"), [3, 2, 1, 0]) + self.assertEqual(pack("RGB", "XRGB"), [0, 1, 2, 3]) + self.assertEqual(pack("RGB", "XBGR"), [0, 3, 2, 1]) - assert_equal(pack("CMYK", "CMYK"), [1, 2, 3, 4]) - assert_equal(pack("YCbCr", "YCbCr"), [1, 2, 3]) + self.assertEqual(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? -def test_unpack(): + self.assertEqual(pack("RGBA", "RGBA"), [1, 2, 3, 4]) - def unpack(mode, rawmode, bytes_): - im = None + self.assertEqual(pack("CMYK", "CMYK"), [1, 2, 3, 4]) + self.assertEqual(pack("YCbCr", "YCbCr"), [1, 2, 3]) - if py3: - data = bytes(range(1,bytes_+1)) - else: - data = ''.join(chr(i) for i in range(1,bytes_+1)) + def test_unpack(self): - im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) + def unpack(mode, rawmode, bytes_): + im = None - return im.getpixel((0, 0)) + if py3: + data = bytes(range(1, bytes_+1)) + else: + data = ''.join(chr(i) for i in range(1, bytes_+1)) - def unpack_1(mode, rawmode, value): - assert mode == "1" - im = None + im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) - if py3: - im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) - else: - im = Image.frombytes(mode, (8, 1), chr(value), "raw", rawmode, 0, 1) + return im.getpixel((0, 0)) - return tuple(im.getdata()) + def unpack_1(mode, rawmode, value): + assert mode == "1" + im = None - X = 255 + if py3: + im = Image.frombytes( + mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) + else: + im = Image.frombytes( + mode, (8, 1), chr(value), "raw", rawmode, 0, 1) - assert_equal(unpack_1("1", "1", 1), (0,0,0,0,0,0,0,X)) - assert_equal(unpack_1("1", "1;I", 1), (X,X,X,X,X,X,X,0)) - assert_equal(unpack_1("1", "1;R", 1), (X,0,0,0,0,0,0,0)) - assert_equal(unpack_1("1", "1;IR", 1), (0,X,X,X,X,X,X,X)) + return tuple(im.getdata()) - assert_equal(unpack_1("1", "1", 170), (X,0,X,0,X,0,X,0)) - assert_equal(unpack_1("1", "1;I", 170), (0,X,0,X,0,X,0,X)) - assert_equal(unpack_1("1", "1;R", 170), (0,X,0,X,0,X,0,X)) - assert_equal(unpack_1("1", "1;IR", 170), (X,0,X,0,X,0,X,0)) + X = 255 - assert_equal(unpack("L", "L;2", 1), 0) - assert_equal(unpack("L", "L;4", 1), 0) - assert_equal(unpack("L", "L", 1), 1) - assert_equal(unpack("L", "L;I", 1), 254) - assert_equal(unpack("L", "L;R", 1), 128) - assert_equal(unpack("L", "L;16", 2), 2) # little endian - assert_equal(unpack("L", "L;16B", 2), 1) # big endian + self.assertEqual(unpack_1("1", "1", 1), (0, 0, 0, 0, 0, 0, 0, X)) + self.assertEqual(unpack_1("1", "1;I", 1), (X, X, X, X, X, X, X, 0)) + self.assertEqual(unpack_1("1", "1;R", 1), (X, 0, 0, 0, 0, 0, 0, 0)) + self.assertEqual(unpack_1("1", "1;IR", 1), (0, X, X, X, X, X, X, X)) - assert_equal(unpack("LA", "LA", 2), (1, 2)) - assert_equal(unpack("LA", "LA;L", 2), (1, 2)) + self.assertEqual(unpack_1("1", "1", 170), (X, 0, X, 0, X, 0, X, 0)) + self.assertEqual(unpack_1("1", "1;I", 170), (0, X, 0, X, 0, X, 0, X)) + self.assertEqual(unpack_1("1", "1;R", 170), (0, X, 0, X, 0, X, 0, X)) + self.assertEqual(unpack_1("1", "1;IR", 170), (X, 0, X, 0, X, 0, X, 0)) - assert_equal(unpack("RGB", "RGB", 3), (1, 2, 3)) - assert_equal(unpack("RGB", "RGB;L", 3), (1, 2, 3)) - assert_equal(unpack("RGB", "RGB;R", 3), (128, 64, 192)) - assert_equal(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? - assert_equal(unpack("RGB", "BGR", 3), (3, 2, 1)) - assert_equal(unpack("RGB", "RGB;15", 2), (8, 131, 0)) - assert_equal(unpack("RGB", "BGR;15", 2), (0, 131, 8)) - assert_equal(unpack("RGB", "RGB;16", 2), (8, 64, 0)) - assert_equal(unpack("RGB", "BGR;16", 2), (0, 64, 8)) - assert_equal(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) + self.assertEqual(unpack("L", "L;2", 1), 0) + self.assertEqual(unpack("L", "L;4", 1), 0) + self.assertEqual(unpack("L", "L", 1), 1) + self.assertEqual(unpack("L", "L;I", 1), 254) + self.assertEqual(unpack("L", "L;R", 1), 128) + self.assertEqual(unpack("L", "L;16", 2), 2) # little endian + self.assertEqual(unpack("L", "L;16B", 2), 1) # big endian - assert_equal(unpack("RGB", "RGBX", 4), (1, 2, 3)) - assert_equal(unpack("RGB", "BGRX", 4), (3, 2, 1)) - assert_equal(unpack("RGB", "XRGB", 4), (2, 3, 4)) - assert_equal(unpack("RGB", "XBGR", 4), (4, 3, 2)) + self.assertEqual(unpack("LA", "LA", 2), (1, 2)) + self.assertEqual(unpack("LA", "LA;L", 2), (1, 2)) - assert_equal(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) - assert_equal(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) - assert_equal(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) - assert_equal(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) - assert_equal(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) - assert_equal(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) - assert_equal(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) + self.assertEqual(unpack("RGB", "RGB", 3), (1, 2, 3)) + self.assertEqual(unpack("RGB", "RGB;L", 3), (1, 2, 3)) + self.assertEqual(unpack("RGB", "RGB;R", 3), (128, 64, 192)) + self.assertEqual(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? + self.assertEqual(unpack("RGB", "BGR", 3), (3, 2, 1)) + self.assertEqual(unpack("RGB", "RGB;15", 2), (8, 131, 0)) + self.assertEqual(unpack("RGB", "BGR;15", 2), (0, 131, 8)) + self.assertEqual(unpack("RGB", "RGB;16", 2), (8, 64, 0)) + self.assertEqual(unpack("RGB", "BGR;16", 2), (0, 64, 8)) + self.assertEqual(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) - assert_equal(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? - assert_equal(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) - assert_equal(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) - assert_equal(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) - assert_equal(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) - assert_equal(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) - assert_equal(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) + self.assertEqual(unpack("RGB", "RGBX", 4), (1, 2, 3)) + self.assertEqual(unpack("RGB", "BGRX", 4), (3, 2, 1)) + self.assertEqual(unpack("RGB", "XRGB", 4), (2, 3, 4)) + self.assertEqual(unpack("RGB", "XBGR", 4), (4, 3, 2)) - assert_equal(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) - assert_equal(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) + self.assertEqual(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) + self.assertEqual(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) + self.assertEqual(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) + self.assertEqual(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) + self.assertEqual(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) + self.assertEqual(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) + self.assertEqual(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) - assert_exception(ValueError, lambda: unpack("L", "L", 0)) - assert_exception(ValueError, lambda: unpack("RGB", "RGB", 2)) - assert_exception(ValueError, lambda: unpack("CMYK", "CMYK", 2)) + self.assertEqual(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? + self.assertEqual(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) + self.assertEqual(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) + self.assertEqual(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) + self.assertEqual(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) + self.assertEqual(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) + self.assertEqual(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) -run() + self.assertEqual(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) + self.assertEqual(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) + + self.assertRaises(ValueError, lambda: unpack("L", "L", 0)) + self.assertRaises(ValueError, lambda: unpack("RGB", "RGB", 2)) + self.assertRaises(ValueError, lambda: unpack("CMYK", "CMYK", 2)) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_locale.py b/Tests/test_locale.py index 6b2b95201..7ef63634b 100644 --- a/Tests/test_locale.py +++ b/Tests/test_locale.py @@ -1,31 +1,39 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena + from PIL import Image import locale # ref https://github.com/python-pillow/Pillow/issues/272 -## on windows, in polish locale: +# on windows, in polish locale: -## import locale -## print locale.setlocale(locale.LC_ALL, 'polish') -## import string -## print len(string.whitespace) -## print ord(string.whitespace[6]) +# import locale +# print locale.setlocale(locale.LC_ALL, 'polish') +# import string +# print len(string.whitespace) +# print ord(string.whitespace[6]) -## Polish_Poland.1250 -## 7 -## 160 +# Polish_Poland.1250 +# 7 +# 160 # one of string.whitespace is not freely convertable into ascii. path = "Images/lena.jpg" -def test_sanity(): - assert_no_exception(lambda: Image.open(path)) - try: - locale.setlocale(locale.LC_ALL, "polish") - except: - skip('polish locale not available') - import string - assert_no_exception(lambda: Image.open(path)) +class TestLocale(PillowTestCase): + + def test_sanity(self): + Image.open(path) + try: + locale.setlocale(locale.LC_ALL, "polish") + except: + unittest.skip('Polish locale not available') + Image.open(path) + + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 782a26623..d8e205b66 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -1,107 +1,109 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image -def verify(im1): - im2 = lena("I") - assert_equal(im1.size, im2.size) - pix1 = im1.load() - pix2 = im2.load() - for y in range(im1.size[1]): - for x in range(im1.size[0]): - xy = x, y - if pix1[xy] != pix2[xy]: - failure( - "got %r from mode %s at %s, expected %r" % - (pix1[xy], im1.mode, xy, pix2[xy]) - ) - return - success() +class TestModeI16(PillowTestCase): + + def verify(self, im1): + im2 = lena("I") + self.assertEqual(im1.size, im2.size) + pix1 = im1.load() + pix2 = im2.load() + for y in range(im1.size[1]): + for x in range(im1.size[0]): + xy = x, y + self.assertEqual( + pix1[xy], pix2[xy], + ("got %r from mode %s at %s, expected %r" % + (pix1[xy], im1.mode, xy, pix2[xy]))) + + def test_basic(self): + # PIL 1.1 has limited support for 16-bit image data. Check that + # create/copy/transform and save works as expected. + + def basic(mode): + + imIn = lena("I").convert(mode) + self.verify(imIn) + + w, h = imIn.size + + imOut = imIn.copy() + self.verify(imOut) # copy + + imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h)) + self.verify(imOut) # transform + + filename = self.tempfile("temp.im") + imIn.save(filename) + + imOut = Image.open(filename) + + self.verify(imIn) + self.verify(imOut) + + imOut = imIn.crop((0, 0, w, h)) + self.verify(imOut) + + imOut = Image.new(mode, (w, h), None) + imOut.paste(imIn.crop((0, 0, w//2, h)), (0, 0)) + imOut.paste(imIn.crop((w//2, 0, w, h)), (w//2, 0)) + + self.verify(imIn) + self.verify(imOut) + + imIn = Image.new(mode, (1, 1), 1) + self.assertEqual(imIn.getpixel((0, 0)), 1) + + imIn.putpixel((0, 0), 2) + self.assertEqual(imIn.getpixel((0, 0)), 2) + + if mode == "L": + max = 255 + else: + max = 32767 + + imIn = Image.new(mode, (1, 1), 256) + self.assertEqual(imIn.getpixel((0, 0)), min(256, max)) + + imIn.putpixel((0, 0), 512) + self.assertEqual(imIn.getpixel((0, 0)), min(512, max)) + + basic("L") + + basic("I;16") + basic("I;16B") + basic("I;16L") + + basic("I") + + def test_tobytes(self): + + def tobytes(mode): + return Image.new(mode, (1, 1), 1).tobytes() + + order = 1 if Image._ENDIAN == '<' else -1 + + self.assertEqual(tobytes("L"), b"\x01") + self.assertEqual(tobytes("I;16"), b"\x01\x00") + self.assertEqual(tobytes("I;16B"), b"\x00\x01") + self.assertEqual(tobytes("I"), b"\x01\x00\x00\x00"[::order]) + + def test_convert(self): + + im = lena("I") + + self.verify(im.convert("I;16")) + self.verify(im.convert("I;16").convert("L")) + self.verify(im.convert("I;16").convert("I")) + + self.verify(im.convert("I;16B")) + self.verify(im.convert("I;16B").convert("L")) + self.verify(im.convert("I;16B").convert("I")) -def test_basic(): - # PIL 1.1 has limited support for 16-bit image data. Check that - # create/copy/transform and save works as expected. +if __name__ == '__main__': + unittest.main() - def basic(mode): - - imIn = lena("I").convert(mode) - verify(imIn) - - w, h = imIn.size - - imOut = imIn.copy() - verify(imOut) # copy - - imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h)) - verify(imOut) # transform - - filename = tempfile("temp.im") - imIn.save(filename) - - imOut = Image.open(filename) - - verify(imIn) - verify(imOut) - - imOut = imIn.crop((0, 0, w, h)) - verify(imOut) - - imOut = Image.new(mode, (w, h), None) - imOut.paste(imIn.crop((0, 0, w//2, h)), (0, 0)) - imOut.paste(imIn.crop((w//2, 0, w, h)), (w//2, 0)) - - verify(imIn) - verify(imOut) - - imIn = Image.new(mode, (1, 1), 1) - assert_equal(imIn.getpixel((0, 0)), 1) - - imIn.putpixel((0, 0), 2) - assert_equal(imIn.getpixel((0, 0)), 2) - - if mode == "L": - max = 255 - else: - max = 32767 - - imIn = Image.new(mode, (1, 1), 256) - assert_equal(imIn.getpixel((0, 0)), min(256, max)) - - imIn.putpixel((0, 0), 512) - assert_equal(imIn.getpixel((0, 0)), min(512, max)) - - basic("L") - - basic("I;16") - basic("I;16B") - basic("I;16L") - - basic("I") - - -def test_tobytes(): - - def tobytes(mode): - return Image.new(mode, (1, 1), 1).tobytes() - - order = 1 if Image._ENDIAN == '<' else -1 - - assert_equal(tobytes("L"), b"\x01") - assert_equal(tobytes("I;16"), b"\x01\x00") - assert_equal(tobytes("I;16B"), b"\x00\x01") - assert_equal(tobytes("I"), b"\x01\x00\x00\x00"[::order]) - - -def test_convert(): - - im = lena("I") - - verify(im.convert("I;16")) - verify(im.convert("I;16").convert("L")) - verify(im.convert("I;16").convert("I")) - - verify(im.convert("I;16B")) - verify(im.convert("I;16B").convert("L")) - verify(im.convert("I;16B").convert("I")) +# End of file diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 9b7881c23..c3c0f7e90 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -1,120 +1,128 @@ -from tester import * +from helper import unittest, PillowTestCase, tearDownModule, lena from PIL import Image -import struct try: import site import numpy except ImportError: - skip() + # Skip via setUp() + pass -def test_numpy_to_image(): - def to_image(dtype, bands=1, bool=0): - if bands == 1: - if bool: - data = [0, 1] * 50 +class TestNumpy(PillowTestCase): + + def setUp(self): + try: + import site + import numpy + except ImportError: + self.skipTest("ImportError") + + def test_numpy_to_image(self): + + def to_image(dtype, bands=1, bool=0): + if bands == 1: + if bool: + data = [0, 1] * 50 + else: + data = list(range(100)) + a = numpy.array(data, dtype=dtype) + a.shape = 10, 10 + i = Image.fromarray(a) + if list(i.getdata()) != data: + print("data mismatch for", dtype) else: data = list(range(100)) - a = numpy.array(data, dtype=dtype) - a.shape = 10, 10 - i = Image.fromarray(a) - if list(i.getdata()) != data: - print("data mismatch for", dtype) + a = numpy.array([[x]*bands for x in data], dtype=dtype) + a.shape = 10, 10, bands + i = Image.fromarray(a) + if list(i.split()[0].getdata()) != list(range(100)): + print("data mismatch for", dtype) + # print dtype, list(i.getdata()) + return i + + # self.assert_image(to_image(numpy.bool, bool=1), "1", (10, 10)) + # self.assert_image(to_image(numpy.bool8, bool=1), "1", (10, 10)) + + self.assertRaises(TypeError, lambda: to_image(numpy.uint)) + self.assert_image(to_image(numpy.uint8), "L", (10, 10)) + self.assertRaises(TypeError, lambda: to_image(numpy.uint16)) + self.assertRaises(TypeError, lambda: to_image(numpy.uint32)) + self.assertRaises(TypeError, lambda: to_image(numpy.uint64)) + + self.assert_image(to_image(numpy.int8), "I", (10, 10)) + if Image._ENDIAN == '<': # Little endian + self.assert_image(to_image(numpy.int16), "I;16", (10, 10)) else: - data = list(range(100)) - a = numpy.array([[x]*bands for x in data], dtype=dtype) - a.shape = 10, 10, bands - i = Image.fromarray(a) - if list(i.split()[0].getdata()) != list(range(100)): - print("data mismatch for", dtype) - # print dtype, list(i.getdata()) - return i + self.assert_image(to_image(numpy.int16), "I;16B", (10, 10)) + self.assert_image(to_image(numpy.int32), "I", (10, 10)) + self.assertRaises(TypeError, lambda: to_image(numpy.int64)) - # assert_image(to_image(numpy.bool, bool=1), "1", (10, 10)) - # assert_image(to_image(numpy.bool8, bool=1), "1", (10, 10)) + self.assert_image(to_image(numpy.float), "F", (10, 10)) + self.assert_image(to_image(numpy.float32), "F", (10, 10)) + self.assert_image(to_image(numpy.float64), "F", (10, 10)) - assert_exception(TypeError, lambda: to_image(numpy.uint)) - assert_image(to_image(numpy.uint8), "L", (10, 10)) - assert_exception(TypeError, lambda: to_image(numpy.uint16)) - assert_exception(TypeError, lambda: to_image(numpy.uint32)) - assert_exception(TypeError, lambda: to_image(numpy.uint64)) + self.assert_image(to_image(numpy.uint8, 3), "RGB", (10, 10)) + self.assert_image(to_image(numpy.uint8, 4), "RGBA", (10, 10)) - assert_image(to_image(numpy.int8), "I", (10, 10)) - if Image._ENDIAN == '<': # Little endian - assert_image(to_image(numpy.int16), "I;16", (10, 10)) - else: - assert_image(to_image(numpy.int16), "I;16B", (10, 10)) - assert_image(to_image(numpy.int32), "I", (10, 10)) - assert_exception(TypeError, lambda: to_image(numpy.int64)) + # based on an erring example at http://is.gd/6F0esS (which resolves to) + # http://stackoverflow.com/questions/10854903/what-is-causing-dimension-dependent-attributeerror-in-pil-fromarray-function + def test_3d_array(self): + a = numpy.ones((10, 10, 10), dtype=numpy.uint8) + self.assert_image(Image.fromarray(a[1, :, :]), "L", (10, 10)) + self.assert_image(Image.fromarray(a[:, 1, :]), "L", (10, 10)) + self.assert_image(Image.fromarray(a[:, :, 1]), "L", (10, 10)) - assert_image(to_image(numpy.float), "F", (10, 10)) - assert_image(to_image(numpy.float32), "F", (10, 10)) - assert_image(to_image(numpy.float64), "F", (10, 10)) + def _test_img_equals_nparray(self, img, np): + self.assertEqual(img.size, np.shape[0:2]) + px = img.load() + for x in range(0, img.size[0], int(img.size[0]/10)): + for y in range(0, img.size[1], int(img.size[1]/10)): + self.assert_deep_equal(px[x, y], np[y, x]) - assert_image(to_image(numpy.uint8, 3), "RGB", (10, 10)) - assert_image(to_image(numpy.uint8, 4), "RGBA", (10, 10)) - - -# based on an erring example at http://is.gd/6F0esS (which resolves to) -# http://stackoverflow.com/questions/10854903/what-is-causing-dimension-dependent-attributeerror-in-pil-fromarray-function -def test_3d_array(): - a = numpy.ones((10, 10, 10), dtype=numpy.uint8) - assert_image(Image.fromarray(a[1, :, :]), "L", (10, 10)) - assert_image(Image.fromarray(a[:, 1, :]), "L", (10, 10)) - assert_image(Image.fromarray(a[:, :, 1]), "L", (10, 10)) - - -def _test_img_equals_nparray(img, np): - assert_equal(img.size, np.shape[0:2]) - px = img.load() - for x in range(0, img.size[0], int(img.size[0]/10)): - for y in range(0, img.size[1], int(img.size[1]/10)): - assert_deep_equal(px[x,y], np[y,x]) - - -def test_16bit(): - img = Image.open('Tests/images/16bit.cropped.tif') - np_img = numpy.array(img) - _test_img_equals_nparray(img, np_img) - assert_equal(np_img.dtype, numpy.dtype('u2'), + ("I;16L", 'u2'), - ("I;16L", ' Date: Tue, 10 Jun 2014 12:12:53 +0300 Subject: [PATCH 143/168] Add test base class and helper functions --- Tests/helper.py | 341 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 Tests/helper.py diff --git a/Tests/helper.py b/Tests/helper.py new file mode 100644 index 000000000..c203e67c2 --- /dev/null +++ b/Tests/helper.py @@ -0,0 +1,341 @@ +""" +Helper functions. +""" +from __future__ import print_function +import sys + +if sys.version_info[:2] <= (2, 6): + import unittest2 as unittest +else: + import unittest + + +def tearDownModule(): + import glob + import os + import tempfile + temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') + tempfiles = glob.glob(os.path.join(temp_root, "temp_*")) + if tempfiles: + print("===", "remaining temporary files") + for file in tempfiles: + print(file) + print("-"*68) + + +class PillowTestCase(unittest.TestCase): + + currentResult = None # holds last result object passed to run method + _tempfiles = [] + + def run(self, result=None): + self.addCleanup(self.delete_tempfiles) + self.currentResult = result # remember result for use later + unittest.TestCase.run(self, result) # call superclass run method + + def delete_tempfiles(self): + try: + ok = self.currentResult.wasSuccessful() + except AttributeError: # for nosetests + proxy = self.currentResult + ok = (len(proxy.errors) + len(proxy.failures) == 0) + + if ok: + # only clean out tempfiles if test passed + import os + import os.path + import tempfile + for file in self._tempfiles: + try: + os.remove(file) + except OSError: + pass # report? + temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') + try: + os.rmdir(temp_root) + except OSError: + pass + + def assert_almost_equal(self, a, b, msg=None, eps=1e-6): + self.assertLess( + abs(a-b), eps, + msg or "got %r, expected %r" % (a, b)) + + def assert_deep_equal(self, a, b, msg=None): + try: + self.assertEqual( + len(a), len(b), + msg or "got length %s, expected %s" % (len(a), len(b))) + self.assertTrue( + all([x == y for x, y in zip(a, b)]), + msg or "got %s, expected %s" % (a, b)) + except: + self.assertEqual(a, b, msg) + + def assert_image(self, im, mode, size, msg=None): + if mode is not None: + self.assertEqual( + im.mode, mode, + msg or "got mode %r, expected %r" % (im.mode, mode)) + + if size is not None: + self.assertEqual( + im.size, size, + msg or "got size %r, expected %r" % (im.size, size)) + + def assert_image_equal(self, a, b, msg=None): + self.assertEqual( + a.mode, b.mode, + msg or "got mode %r, expected %r" % (a.mode, b.mode)) + self.assertEqual( + a.size, b.size, + msg or "got size %r, expected %r" % (a.size, b.size)) + self.assertEqual( + a.tobytes(), b.tobytes(), + msg or "got different content") + + def assert_image_similar(self, a, b, epsilon, msg=None): + epsilon = float(epsilon) + self.assertEqual( + a.mode, b.mode, + msg or "got mode %r, expected %r" % (a.mode, b.mode)) + self.assertEqual( + a.size, b.size, + msg or "got size %r, expected %r" % (a.size, b.size)) + + diff = 0 + try: + ord(b'0') + for abyte, bbyte in zip(a.tobytes(), b.tobytes()): + diff += abs(ord(abyte)-ord(bbyte)) + except: + for abyte, bbyte in zip(a.tobytes(), b.tobytes()): + diff += abs(abyte-bbyte) + ave_diff = float(diff)/(a.size[0]*a.size[1]) + self.assertGreaterEqual( + epsilon, ave_diff, + msg or "average pixel value difference %.4f > epsilon %.4f" % ( + ave_diff, epsilon)) + + def assert_warning(self, warn_class, func): + import warnings + + result = None + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + + # Hopefully trigger a warning. + result = func() + + # Verify some things. + self.assertGreaterEqual(len(w), 1) + found = False + for v in w: + if issubclass(v.category, warn_class): + found = True + break + self.assertTrue(found) + return result + + def tempfile(self, template, *extra): + import os + import os.path + import sys + import tempfile + files = [] + root = os.path.join(tempfile.gettempdir(), 'pillow-tests') + try: + os.mkdir(root) + except OSError: + pass + for temp in (template,) + extra: + assert temp[:5] in ("temp.", "temp_") + name = os.path.basename(sys.argv[0]) + name = temp[:4] + os.path.splitext(name)[0][4:] + name = name + "_%d" % len(self._tempfiles) + temp[4:] + name = os.path.join(root, name) + files.append(name) + self._tempfiles.extend(files) + return files[0] + + +# # require that deprecation warnings are triggered +# import warnings +# warnings.simplefilter('default') +# # temporarily turn off resource warnings that warn about unclosed +# # files in the test scripts. +# try: +# warnings.filterwarnings("ignore", category=ResourceWarning) +# except NameError: +# # we expect a NameError on py2.x, since it doesn't have ResourceWarnings. +# pass + +import sys +py3 = (sys.version_info >= (3, 0)) + +# # some test helpers +# +# _target = None +# _tempfiles = [] +# _logfile = None +# +# +# def success(): +# import sys +# success.count += 1 +# if _logfile: +# print(sys.argv[0], success.count, failure.count, file=_logfile) +# return True +# +# +# def failure(msg=None, frame=None): +# import sys +# import linecache +# failure.count += 1 +# if _target: +# if frame is None: +# frame = sys._getframe() +# while frame.f_globals.get("__name__") != _target.__name__: +# frame = frame.f_back +# location = (frame.f_code.co_filename, frame.f_lineno) +# prefix = "%s:%d: " % location +# line = linecache.getline(*location) +# print(prefix + line.strip() + " failed:") +# if msg: +# print("- " + msg) +# if _logfile: +# print(sys.argv[0], success.count, failure.count, file=_logfile) +# return False +# +# success.count = failure.count = 0 +# + + +# helpers + +def fromstring(data): + from io import BytesIO + from PIL import Image + return Image.open(BytesIO(data)) + + +def tostring(im, format, **options): + from io import BytesIO + out = BytesIO() + im.save(out, format, **options) + return out.getvalue() + + +def lena(mode="RGB", cache={}): + from PIL import Image + im = None + # im = cache.get(mode) + if im is None: + if mode == "RGB": + im = Image.open("Images/lena.ppm") + elif mode == "F": + im = lena("L").convert(mode) + elif mode[:4] == "I;16": + im = lena("I").convert(mode) + else: + im = lena("RGB").convert(mode) + # cache[mode] = im + return im + + +# def assert_image_completely_equal(a, b, msg=None): +# if a != b: +# failure(msg or "images different") +# else: +# success() +# +# +# # test runner +# +# def run(): +# global _target, _tests, run +# import sys +# import traceback +# _target = sys.modules["__main__"] +# run = None # no need to run twice +# tests = [] +# for name, value in list(vars(_target).items()): +# if name[:5] == "test_" and type(value) is type(success): +# tests.append((value.__code__.co_firstlineno, name, value)) +# tests.sort() # sort by line +# for lineno, name, func in tests: +# try: +# _tests = [] +# func() +# for func, args in _tests: +# func(*args) +# except: +# t, v, tb = sys.exc_info() +# tb = tb.tb_next +# if tb: +# failure(frame=tb.tb_frame) +# traceback.print_exception(t, v, tb) +# else: +# print("%s:%d: cannot call test function: %s" % ( +# sys.argv[0], lineno, v)) +# failure.count += 1 +# +# +# def yield_test(function, *args): +# # collect delayed/generated tests +# _tests.append((function, args)) +# +# +# def skip(msg=None): +# import os +# print("skip") +# os._exit(0) # don't run exit handlers +# +# +# def ignore(pattern): +# """Tells the driver to ignore messages matching the pattern, for the +# duration of the current test.""" +# print('ignore: %s' % pattern) +# +# +# def _setup(): +# global _logfile +# +# import sys +# if "--coverage" in sys.argv: +# # Temporary: ignore PendingDeprecationWarning from Coverage (Py3.4) +# with warnings.catch_warnings(): +# warnings.simplefilter("ignore") +# import coverage +# cov = coverage.coverage(auto_data=True, include="PIL/*") +# cov.start() +# +# def report(): +# if run: +# run() +# if success.count and not failure.count: +# print("ok") +# # only clean out tempfiles if test passed +# import os +# import os.path +# import tempfile +# for file in _tempfiles: +# try: +# os.remove(file) +# except OSError: +# pass # report? +# temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') +# try: +# os.rmdir(temp_root) +# except OSError: +# pass +# +# import atexit +# atexit.register(report) +# +# if "--log" in sys.argv: +# _logfile = open("test.log", "a") +# +# +# _setup() From 1686016f1c6fc7ef31776474fdd62f5622eb0304 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 10 Jun 2014 12:20:51 +0300 Subject: [PATCH 144/168] Remove old tester.py so it's not picked up by nosetests as a test --- Tests/tester.py | 388 ------------------------------------------------ 1 file changed, 388 deletions(-) delete mode 100644 Tests/tester.py diff --git a/Tests/tester.py b/Tests/tester.py deleted file mode 100644 index 4476aced1..000000000 --- a/Tests/tester.py +++ /dev/null @@ -1,388 +0,0 @@ -from __future__ import print_function - -# require that deprecation warnings are triggered -import warnings -warnings.simplefilter('default') -# temporarily turn off resource warnings that warn about unclosed -# files in the test scripts. -try: - warnings.filterwarnings("ignore", category=ResourceWarning) -except NameError: - # we expect a NameError on py2.x, since it doesn't have ResourceWarnings. - pass - -import sys -py3 = (sys.version_info >= (3, 0)) - -# some test helpers - -_target = None -_tempfiles = [] -_logfile = None - - -def success(): - import sys - success.count += 1 - if _logfile: - print(sys.argv[0], success.count, failure.count, file=_logfile) - return True - - -def failure(msg=None, frame=None): - import sys - import linecache - failure.count += 1 - if _target: - if frame is None: - frame = sys._getframe() - while frame.f_globals.get("__name__") != _target.__name__: - frame = frame.f_back - location = (frame.f_code.co_filename, frame.f_lineno) - prefix = "%s:%d: " % location - line = linecache.getline(*location) - print(prefix + line.strip() + " failed:") - if msg: - print("- " + msg) - if _logfile: - print(sys.argv[0], success.count, failure.count, file=_logfile) - return False - -success.count = failure.count = 0 - - -# predicates - -def assert_true(v, msg=None): - if v: - success() - else: - failure(msg or "got %r, expected true value" % v) - - -def assert_false(v, msg=None): - if v: - failure(msg or "got %r, expected false value" % v) - else: - success() - - -def assert_equal(a, b, msg=None): - if a == b: - success() - else: - failure(msg or "got %r, expected %r" % (a, b)) - - -def assert_almost_equal(a, b, msg=None, eps=1e-6): - if abs(a-b) < eps: - success() - else: - failure(msg or "got %r, expected %r" % (a, b)) - - -def assert_deep_equal(a, b, msg=None): - try: - if len(a) == len(b): - if all([x == y for x, y in zip(a, b)]): - success() - else: - failure(msg or "got %s, expected %s" % (a, b)) - else: - failure(msg or "got length %s, expected %s" % (len(a), len(b))) - except: - assert_equal(a, b, msg) - - -def assert_greater(a, b, msg=None): - if a > b: - success() - else: - failure(msg or "%r unexpectedly not greater than %r" % (a, b)) - - -def assert_greater_equal(a, b, msg=None): - if a >= b: - success() - else: - failure( - msg or "%r unexpectedly not greater than or equal to %r" % (a, b)) - - -def assert_less(a, b, msg=None): - if a < b: - success() - else: - failure(msg or "%r unexpectedly not less than %r" % (a, b)) - - -def assert_less_equal(a, b, msg=None): - if a <= b: - success() - else: - failure( - msg or "%r unexpectedly not less than or equal to %r" % (a, b)) - - -def assert_is_instance(a, b, msg=None): - if isinstance(a, b): - success() - else: - failure(msg or "got %r, expected %r" % (type(a), b)) - - -def assert_in(a, b, msg=None): - if a in b: - success() - else: - failure(msg or "%r unexpectedly not in %r" % (a, b)) - - -def assert_match(v, pattern, msg=None): - import re - if re.match(pattern, v): - success() - else: - failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) - - -def assert_exception(exc_class, func): - import sys - import traceback - try: - func() - except exc_class: - success() - except: - failure("expected %r exception, got %r" % ( - exc_class.__name__, sys.exc_info()[0].__name__)) - traceback.print_exc() - else: - failure("expected %r exception, got no exception" % exc_class.__name__) - - -def assert_no_exception(func): - import sys - import traceback - try: - func() - except: - failure("expected no exception, got %r" % sys.exc_info()[0].__name__) - traceback.print_exc() - else: - success() - - -def assert_warning(warn_class, func): - # note: this assert calls func three times! - import warnings - - def warn_error(message, category=UserWarning, **options): - raise category(message) - - def warn_ignore(message, category=UserWarning, **options): - pass - warn = warnings.warn - result = None - try: - warnings.warn = warn_ignore - assert_no_exception(func) - result = func() - warnings.warn = warn_error - assert_exception(warn_class, func) - finally: - warnings.warn = warn # restore - return result - -# helpers - -from io import BytesIO - - -def fromstring(data): - from PIL import Image - return Image.open(BytesIO(data)) - - -def tostring(im, format, **options): - out = BytesIO() - im.save(out, format, **options) - return out.getvalue() - - -def lena(mode="RGB", cache={}): - from PIL import Image - im = cache.get(mode) - if im is None: - if mode == "RGB": - im = Image.open("Images/lena.ppm") - elif mode == "F": - im = lena("L").convert(mode) - elif mode[:4] == "I;16": - im = lena("I").convert(mode) - else: - im = lena("RGB").convert(mode) - cache[mode] = im - return im - - -def assert_image(im, mode, size, msg=None): - if mode is not None and im.mode != mode: - failure(msg or "got mode %r, expected %r" % (im.mode, mode)) - elif size is not None and im.size != size: - failure(msg or "got size %r, expected %r" % (im.size, size)) - else: - success() - - -def assert_image_equal(a, b, msg=None): - if a.mode != b.mode: - failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) - elif a.size != b.size: - failure(msg or "got size %r, expected %r" % (a.size, b.size)) - elif a.tobytes() != b.tobytes(): - failure(msg or "got different content") - else: - success() - - -def assert_image_completely_equal(a, b, msg=None): - if a != b: - failure(msg or "images different") - else: - success() - - -def assert_image_similar(a, b, epsilon, msg=None): - epsilon = float(epsilon) - if a.mode != b.mode: - return failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) - elif a.size != b.size: - return failure(msg or "got size %r, expected %r" % (a.size, b.size)) - diff = 0 - try: - ord(b'0') - for abyte, bbyte in zip(a.tobytes(), b.tobytes()): - diff += abs(ord(abyte)-ord(bbyte)) - except: - for abyte, bbyte in zip(a.tobytes(), b.tobytes()): - diff += abs(abyte-bbyte) - ave_diff = float(diff)/(a.size[0]*a.size[1]) - if epsilon < ave_diff: - return failure( - msg or "average pixel value difference %.4f > epsilon %.4f" % ( - ave_diff, epsilon)) - else: - return success() - - -def tempfile(template, *extra): - import os - import os.path - import sys - import tempfile - files = [] - root = os.path.join(tempfile.gettempdir(), 'pillow-tests') - try: - os.mkdir(root) - except OSError: - pass - for temp in (template,) + extra: - assert temp[:5] in ("temp.", "temp_") - name = os.path.basename(sys.argv[0]) - name = temp[:4] + os.path.splitext(name)[0][4:] - name = name + "_%d" % len(_tempfiles) + temp[4:] - name = os.path.join(root, name) - files.append(name) - _tempfiles.extend(files) - return files[0] - - -# test runner - -def run(): - global _target, _tests, run - import sys - import traceback - _target = sys.modules["__main__"] - run = None # no need to run twice - tests = [] - for name, value in list(vars(_target).items()): - if name[:5] == "test_" and type(value) is type(success): - tests.append((value.__code__.co_firstlineno, name, value)) - tests.sort() # sort by line - for lineno, name, func in tests: - try: - _tests = [] - func() - for func, args in _tests: - func(*args) - except: - t, v, tb = sys.exc_info() - tb = tb.tb_next - if tb: - failure(frame=tb.tb_frame) - traceback.print_exception(t, v, tb) - else: - print("%s:%d: cannot call test function: %s" % ( - sys.argv[0], lineno, v)) - failure.count += 1 - - -def yield_test(function, *args): - # collect delayed/generated tests - _tests.append((function, args)) - - -def skip(msg=None): - import os - print("skip") - os._exit(0) # don't run exit handlers - - -def ignore(pattern): - """Tells the driver to ignore messages matching the pattern, for the - duration of the current test.""" - print('ignore: %s' % pattern) - - -def _setup(): - global _logfile - - import sys - if "--coverage" in sys.argv: - # Temporary: ignore PendingDeprecationWarning from Coverage (Py3.4) - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - import coverage - cov = coverage.coverage(auto_data=True, include="PIL/*") - cov.start() - - def report(): - if run: - run() - if success.count and not failure.count: - print("ok") - # only clean out tempfiles if test passed - import os - import os.path - import tempfile - for file in _tempfiles: - try: - os.remove(file) - except OSError: - pass # report? - temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') - try: - os.rmdir(temp_root) - except OSError: - pass - - import atexit - atexit.register(report) - - if "--log" in sys.argv: - _logfile = open("test.log", "a") - - -_setup() From 0878dfd01062d3344b8bbc15d9f1bd485c132dd9 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 10 Jun 2014 12:26:50 +0300 Subject: [PATCH 145/168] Update Travis CI to run new tests based on unittest --- .travis.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d313a088..284378266 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,8 @@ python: install: - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" - "pip install cffi" - - "pip install coveralls" + - "pip install coveralls nose" + - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then pip install unittest2; fi # webp - pushd depends && ./install_webp.sh && popd @@ -28,18 +29,18 @@ script: - python setup.py build_ext --inplace # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time python selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests test/; fi # Cover the others - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose test/; fi after_success: - coverage report - coveralls - pip install pep8 pyflakes - pep8 --statistics --count PIL/*.py - - pep8 --statistics --count Tests/*.py + - pep8 --statistics --count Tests/*.py - pyflakes PIL/*.py | tee >(wc -l) - pyflakes Tests/*.py | tee >(wc -l) From c8b94113212e88e95f12eac13ad19542296209a3 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 10 Jun 2014 12:38:20 +0300 Subject: [PATCH 146/168] Fix test path for Travis CI --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 284378266..576e6e974 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,11 +30,11 @@ script: # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time python selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests test/; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests Tests/; fi # Cover the others - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose test/; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose Tests/; fi after_success: - coverage report From 7178279b21196a811f2c2b1942248fedd2c72b8d Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 10 Jun 2014 13:02:52 +0300 Subject: [PATCH 147/168] Specify tests more closely to avoid things like Tests/cms_test.py --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 576e6e974..389051358 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,11 +30,11 @@ script: # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time python selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests Tests/; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests Tests/test_*.py; fi # Cover the others - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose Tests/; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose Tests/test_*.py; fi after_success: - coverage report From 77948317044f71133a31a0b2c770dbb5dc0940e3 Mon Sep 17 00:00:00 2001 From: hugovk Date: Tue, 10 Jun 2014 13:36:38 +0300 Subject: [PATCH 148/168] Bring back tester.py for now. Not using it for unit tests. --- Tests/tester.py | 388 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 Tests/tester.py diff --git a/Tests/tester.py b/Tests/tester.py new file mode 100644 index 000000000..4476aced1 --- /dev/null +++ b/Tests/tester.py @@ -0,0 +1,388 @@ +from __future__ import print_function + +# require that deprecation warnings are triggered +import warnings +warnings.simplefilter('default') +# temporarily turn off resource warnings that warn about unclosed +# files in the test scripts. +try: + warnings.filterwarnings("ignore", category=ResourceWarning) +except NameError: + # we expect a NameError on py2.x, since it doesn't have ResourceWarnings. + pass + +import sys +py3 = (sys.version_info >= (3, 0)) + +# some test helpers + +_target = None +_tempfiles = [] +_logfile = None + + +def success(): + import sys + success.count += 1 + if _logfile: + print(sys.argv[0], success.count, failure.count, file=_logfile) + return True + + +def failure(msg=None, frame=None): + import sys + import linecache + failure.count += 1 + if _target: + if frame is None: + frame = sys._getframe() + while frame.f_globals.get("__name__") != _target.__name__: + frame = frame.f_back + location = (frame.f_code.co_filename, frame.f_lineno) + prefix = "%s:%d: " % location + line = linecache.getline(*location) + print(prefix + line.strip() + " failed:") + if msg: + print("- " + msg) + if _logfile: + print(sys.argv[0], success.count, failure.count, file=_logfile) + return False + +success.count = failure.count = 0 + + +# predicates + +def assert_true(v, msg=None): + if v: + success() + else: + failure(msg or "got %r, expected true value" % v) + + +def assert_false(v, msg=None): + if v: + failure(msg or "got %r, expected false value" % v) + else: + success() + + +def assert_equal(a, b, msg=None): + if a == b: + success() + else: + failure(msg or "got %r, expected %r" % (a, b)) + + +def assert_almost_equal(a, b, msg=None, eps=1e-6): + if abs(a-b) < eps: + success() + else: + failure(msg or "got %r, expected %r" % (a, b)) + + +def assert_deep_equal(a, b, msg=None): + try: + if len(a) == len(b): + if all([x == y for x, y in zip(a, b)]): + success() + else: + failure(msg or "got %s, expected %s" % (a, b)) + else: + failure(msg or "got length %s, expected %s" % (len(a), len(b))) + except: + assert_equal(a, b, msg) + + +def assert_greater(a, b, msg=None): + if a > b: + success() + else: + failure(msg or "%r unexpectedly not greater than %r" % (a, b)) + + +def assert_greater_equal(a, b, msg=None): + if a >= b: + success() + else: + failure( + msg or "%r unexpectedly not greater than or equal to %r" % (a, b)) + + +def assert_less(a, b, msg=None): + if a < b: + success() + else: + failure(msg or "%r unexpectedly not less than %r" % (a, b)) + + +def assert_less_equal(a, b, msg=None): + if a <= b: + success() + else: + failure( + msg or "%r unexpectedly not less than or equal to %r" % (a, b)) + + +def assert_is_instance(a, b, msg=None): + if isinstance(a, b): + success() + else: + failure(msg or "got %r, expected %r" % (type(a), b)) + + +def assert_in(a, b, msg=None): + if a in b: + success() + else: + failure(msg or "%r unexpectedly not in %r" % (a, b)) + + +def assert_match(v, pattern, msg=None): + import re + if re.match(pattern, v): + success() + else: + failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) + + +def assert_exception(exc_class, func): + import sys + import traceback + try: + func() + except exc_class: + success() + except: + failure("expected %r exception, got %r" % ( + exc_class.__name__, sys.exc_info()[0].__name__)) + traceback.print_exc() + else: + failure("expected %r exception, got no exception" % exc_class.__name__) + + +def assert_no_exception(func): + import sys + import traceback + try: + func() + except: + failure("expected no exception, got %r" % sys.exc_info()[0].__name__) + traceback.print_exc() + else: + success() + + +def assert_warning(warn_class, func): + # note: this assert calls func three times! + import warnings + + def warn_error(message, category=UserWarning, **options): + raise category(message) + + def warn_ignore(message, category=UserWarning, **options): + pass + warn = warnings.warn + result = None + try: + warnings.warn = warn_ignore + assert_no_exception(func) + result = func() + warnings.warn = warn_error + assert_exception(warn_class, func) + finally: + warnings.warn = warn # restore + return result + +# helpers + +from io import BytesIO + + +def fromstring(data): + from PIL import Image + return Image.open(BytesIO(data)) + + +def tostring(im, format, **options): + out = BytesIO() + im.save(out, format, **options) + return out.getvalue() + + +def lena(mode="RGB", cache={}): + from PIL import Image + im = cache.get(mode) + if im is None: + if mode == "RGB": + im = Image.open("Images/lena.ppm") + elif mode == "F": + im = lena("L").convert(mode) + elif mode[:4] == "I;16": + im = lena("I").convert(mode) + else: + im = lena("RGB").convert(mode) + cache[mode] = im + return im + + +def assert_image(im, mode, size, msg=None): + if mode is not None and im.mode != mode: + failure(msg or "got mode %r, expected %r" % (im.mode, mode)) + elif size is not None and im.size != size: + failure(msg or "got size %r, expected %r" % (im.size, size)) + else: + success() + + +def assert_image_equal(a, b, msg=None): + if a.mode != b.mode: + failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) + elif a.size != b.size: + failure(msg or "got size %r, expected %r" % (a.size, b.size)) + elif a.tobytes() != b.tobytes(): + failure(msg or "got different content") + else: + success() + + +def assert_image_completely_equal(a, b, msg=None): + if a != b: + failure(msg or "images different") + else: + success() + + +def assert_image_similar(a, b, epsilon, msg=None): + epsilon = float(epsilon) + if a.mode != b.mode: + return failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) + elif a.size != b.size: + return failure(msg or "got size %r, expected %r" % (a.size, b.size)) + diff = 0 + try: + ord(b'0') + for abyte, bbyte in zip(a.tobytes(), b.tobytes()): + diff += abs(ord(abyte)-ord(bbyte)) + except: + for abyte, bbyte in zip(a.tobytes(), b.tobytes()): + diff += abs(abyte-bbyte) + ave_diff = float(diff)/(a.size[0]*a.size[1]) + if epsilon < ave_diff: + return failure( + msg or "average pixel value difference %.4f > epsilon %.4f" % ( + ave_diff, epsilon)) + else: + return success() + + +def tempfile(template, *extra): + import os + import os.path + import sys + import tempfile + files = [] + root = os.path.join(tempfile.gettempdir(), 'pillow-tests') + try: + os.mkdir(root) + except OSError: + pass + for temp in (template,) + extra: + assert temp[:5] in ("temp.", "temp_") + name = os.path.basename(sys.argv[0]) + name = temp[:4] + os.path.splitext(name)[0][4:] + name = name + "_%d" % len(_tempfiles) + temp[4:] + name = os.path.join(root, name) + files.append(name) + _tempfiles.extend(files) + return files[0] + + +# test runner + +def run(): + global _target, _tests, run + import sys + import traceback + _target = sys.modules["__main__"] + run = None # no need to run twice + tests = [] + for name, value in list(vars(_target).items()): + if name[:5] == "test_" and type(value) is type(success): + tests.append((value.__code__.co_firstlineno, name, value)) + tests.sort() # sort by line + for lineno, name, func in tests: + try: + _tests = [] + func() + for func, args in _tests: + func(*args) + except: + t, v, tb = sys.exc_info() + tb = tb.tb_next + if tb: + failure(frame=tb.tb_frame) + traceback.print_exception(t, v, tb) + else: + print("%s:%d: cannot call test function: %s" % ( + sys.argv[0], lineno, v)) + failure.count += 1 + + +def yield_test(function, *args): + # collect delayed/generated tests + _tests.append((function, args)) + + +def skip(msg=None): + import os + print("skip") + os._exit(0) # don't run exit handlers + + +def ignore(pattern): + """Tells the driver to ignore messages matching the pattern, for the + duration of the current test.""" + print('ignore: %s' % pattern) + + +def _setup(): + global _logfile + + import sys + if "--coverage" in sys.argv: + # Temporary: ignore PendingDeprecationWarning from Coverage (Py3.4) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + import coverage + cov = coverage.coverage(auto_data=True, include="PIL/*") + cov.start() + + def report(): + if run: + run() + if success.count and not failure.count: + print("ok") + # only clean out tempfiles if test passed + import os + import os.path + import tempfile + for file in _tempfiles: + try: + os.remove(file) + except OSError: + pass # report? + temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') + try: + os.rmdir(temp_root) + except OSError: + pass + + import atexit + atexit.register(report) + + if "--log" in sys.argv: + _logfile = open("test.log", "a") + + +_setup() From 3ff590d253e30c149ca9e45f3899666e9a862f34 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 10 Jun 2014 07:42:57 -0400 Subject: [PATCH 149/168] Revert "Update" This reverts commit 1d96522932927f18eb2b7f3b35608ab94a9a86ce. --- CHANGES.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 40d8ed5cd..71a42e315 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,9 +4,6 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ -- Use unittest for tests - [hugovk] - - ImageCms fixes [hugovk] From b2a2f16b234b44d59cd861c97b3bb849e3ca1504 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 10 Jun 2014 07:43:23 -0400 Subject: [PATCH 150/168] Revert "Merge pull request #693 from hugovk/unittest0" This reverts commit 001b46c6708deb96442549d9acb55d7d502e1f6c, reversing changes made to 8beb66443becbd588c4148267fd2ba29c602be57. --- .travis.yml | 18 +- PIL/tests.py | 17 ++ Tests/test_000_sanity.py | 24 +++ Tests/test_bmp_reference.py | 86 +++++++++ Tests/test_file_fli.py | 14 ++ Tests/test_file_icns.py | 66 +++++++ Tests/test_file_ico.py | 14 ++ Tests/test_file_psd.py | 14 ++ {test => Tests}/test_file_xbm.py | 22 +-- Tests/test_file_xpm.py | 14 ++ Tests/test_font_bdf.py | 13 ++ Tests/test_format_lab.py | 41 ++++ Tests/test_image_array.py | 33 ++++ Tests/test_image_copy.py | 12 ++ Tests/test_image_crop.py | 52 ++++++ Tests/test_image_filter.py | 82 ++++++++ Tests/test_image_frombytes.py | 10 + Tests/test_image_getbands.py | 15 ++ Tests/test_image_getbbox.py | 36 ++++ Tests/test_image_getcolors.py | 64 +++++++ Tests/test_image_getextrema.py | 17 ++ Tests/test_image_getim.py | 14 ++ Tests/test_image_getpalette.py | 19 ++ Tests/test_image_histogram.py | 19 ++ Tests/test_image_load.py | 27 +++ Tests/test_image_mode.py | 27 +++ Tests/test_image_offset.py | 16 ++ Tests/test_image_paste.py | 5 + Tests/test_image_putalpha.py | 43 +++++ Tests/test_image_putdata.py | 40 ++++ Tests/test_image_putpalette.py | 28 +++ Tests/test_image_quantize.py | 27 +++ Tests/test_image_resize.py | 12 ++ Tests/test_image_rotate.py | 15 ++ Tests/test_image_save.py | 5 + Tests/test_image_seek.py | 5 + Tests/test_image_show.py | 5 + Tests/test_image_tell.py | 5 + Tests/test_image_thumbnail.py | 36 ++++ Tests/test_image_tobitmap.py | 15 ++ Tests/test_image_tobytes.py | 7 + Tests/test_image_transform.py | 116 ++++++++++++ Tests/test_image_verify.py | 5 + Tests/test_imagechops.py | 56 ++++++ Tests/test_imagecms.py | 203 ++++++++++++++++++++ Tests/test_imagecolor.py | 54 ++++++ Tests/test_imagedraw.py | 270 +++++++++++++++++++++++++++ Tests/test_imageenhance.py | 19 ++ Tests/test_imagefileio.py | 24 +++ Tests/test_imagefilter.py | 31 ++++ Tests/test_imagefont.py | 135 ++++++++++++++ Tests/test_imagegrab.py | 13 ++ Tests/test_imagemath.py | 62 +++++++ Tests/test_imagemode.py | 23 +++ Tests/test_imageops.py | 81 ++++++++ Tests/test_imageshow.py | 6 + Tests/test_imagestat.py | 52 ++++++ Tests/test_imagetk.py | 9 + Tests/test_imagetransform.py | 18 ++ Tests/test_imagewin.py | 6 + Tests/test_lib_image.py | 30 +++ Tests/test_lib_pack.py | 138 ++++++++++++++ Tests/test_locale.py | 31 ++++ test/helper.py | 310 ------------------------------- test/test_000_sanity.py | 32 ---- test/test_bmp_reference.py | 94 ---------- test/test_file_fli.py | 23 --- test/test_file_icns.py | 74 -------- test/test_file_ico.py | 23 --- test/test_file_psd.py | 23 --- test/test_file_xpm.py | 23 --- test/test_font_bdf.py | 22 --- test/test_format_lab.py | 48 ----- test/test_image_array.py | 46 ----- test/test_image_copy.py | 20 -- test/test_image_crop.py | 59 ------ test/test_image_filter.py | 91 --------- test/test_image_frombytes.py | 18 -- test/test_image_getbands.py | 26 --- test/test_image_getbbox.py | 45 ----- test/test_image_getcolors.py | 74 -------- test/test_image_getextrema.py | 27 --- test/test_image_getim.py | 19 -- test/test_image_getpalette.py | 26 --- test/test_image_histogram.py | 26 --- test/test_image_load.py | 35 ---- test/test_image_mode.py | 36 ---- test/test_image_offset.py | 25 --- test/test_image_putalpha.py | 52 ------ test/test_image_putdata.py | 48 ----- test/test_image_putpalette.py | 36 ---- test/test_image_quantize.py | 35 ---- test/test_image_resize.py | 19 -- test/test_image_rotate.py | 22 --- test/test_image_thumbnail.py | 43 ----- test/test_image_tobitmap.py | 22 --- test/test_image_tobytes.py | 13 -- test/test_image_transform.py | 125 ------------- test/test_imagechops.py | 74 -------- test/test_imagecms.py | 214 --------------------- test/test_imagecolor.py | 71 ------- test/test_imagedraw.py | 255 ------------------------- test/test_imageenhance.py | 28 --- test/test_imagefileio.py | 33 ---- test/test_imagefilter.py | 37 ---- test/test_imagefont.py | 145 --------------- test/test_imagegrab.py | 25 --- test/test_imagemath.py | 78 -------- test/test_imagemode.py | 32 ---- test/test_imageops.py | 85 --------- test/test_imageshow.py | 18 -- test/test_imagestat.py | 63 ------- test/test_imagetk.py | 17 -- test/test_imagetransform.py | 27 --- test/test_imagewin.py | 18 -- test/test_lib_image.py | 39 ---- test/test_lib_pack.py | 147 --------------- test/test_locale.py | 39 ---- 118 files changed, 2388 insertions(+), 3133 deletions(-) create mode 100644 PIL/tests.py create mode 100644 Tests/test_000_sanity.py create mode 100644 Tests/test_bmp_reference.py create mode 100644 Tests/test_file_fli.py create mode 100644 Tests/test_file_icns.py create mode 100644 Tests/test_file_ico.py create mode 100644 Tests/test_file_psd.py rename {test => Tests}/test_file_xbm.py (72%) create mode 100644 Tests/test_file_xpm.py create mode 100644 Tests/test_font_bdf.py create mode 100644 Tests/test_format_lab.py create mode 100644 Tests/test_image_array.py create mode 100644 Tests/test_image_copy.py create mode 100644 Tests/test_image_crop.py create mode 100644 Tests/test_image_filter.py create mode 100644 Tests/test_image_frombytes.py create mode 100644 Tests/test_image_getbands.py create mode 100644 Tests/test_image_getbbox.py create mode 100644 Tests/test_image_getcolors.py create mode 100644 Tests/test_image_getextrema.py create mode 100644 Tests/test_image_getim.py create mode 100644 Tests/test_image_getpalette.py create mode 100644 Tests/test_image_histogram.py create mode 100644 Tests/test_image_load.py create mode 100644 Tests/test_image_mode.py create mode 100644 Tests/test_image_offset.py create mode 100644 Tests/test_image_paste.py create mode 100644 Tests/test_image_putalpha.py create mode 100644 Tests/test_image_putdata.py create mode 100644 Tests/test_image_putpalette.py create mode 100644 Tests/test_image_quantize.py create mode 100644 Tests/test_image_resize.py create mode 100644 Tests/test_image_rotate.py create mode 100644 Tests/test_image_save.py create mode 100644 Tests/test_image_seek.py create mode 100644 Tests/test_image_show.py create mode 100644 Tests/test_image_tell.py create mode 100644 Tests/test_image_thumbnail.py create mode 100644 Tests/test_image_tobitmap.py create mode 100644 Tests/test_image_tobytes.py create mode 100644 Tests/test_image_transform.py create mode 100644 Tests/test_image_verify.py create mode 100644 Tests/test_imagechops.py create mode 100644 Tests/test_imagecms.py create mode 100644 Tests/test_imagecolor.py create mode 100644 Tests/test_imagedraw.py create mode 100644 Tests/test_imageenhance.py create mode 100644 Tests/test_imagefileio.py create mode 100644 Tests/test_imagefilter.py create mode 100644 Tests/test_imagefont.py create mode 100644 Tests/test_imagegrab.py create mode 100644 Tests/test_imagemath.py create mode 100644 Tests/test_imagemode.py create mode 100644 Tests/test_imageops.py create mode 100644 Tests/test_imageshow.py create mode 100644 Tests/test_imagestat.py create mode 100644 Tests/test_imagetk.py create mode 100644 Tests/test_imagetransform.py create mode 100644 Tests/test_imagewin.py create mode 100644 Tests/test_lib_image.py create mode 100644 Tests/test_lib_pack.py create mode 100644 Tests/test_locale.py delete mode 100644 test/helper.py delete mode 100644 test/test_000_sanity.py delete mode 100644 test/test_bmp_reference.py delete mode 100644 test/test_file_fli.py delete mode 100644 test/test_file_icns.py delete mode 100644 test/test_file_ico.py delete mode 100644 test/test_file_psd.py delete mode 100644 test/test_file_xpm.py delete mode 100644 test/test_font_bdf.py delete mode 100644 test/test_format_lab.py delete mode 100644 test/test_image_array.py delete mode 100644 test/test_image_copy.py delete mode 100644 test/test_image_crop.py delete mode 100644 test/test_image_filter.py delete mode 100644 test/test_image_frombytes.py delete mode 100644 test/test_image_getbands.py delete mode 100644 test/test_image_getbbox.py delete mode 100644 test/test_image_getcolors.py delete mode 100644 test/test_image_getextrema.py delete mode 100644 test/test_image_getim.py delete mode 100644 test/test_image_getpalette.py delete mode 100644 test/test_image_histogram.py delete mode 100644 test/test_image_load.py delete mode 100644 test/test_image_mode.py delete mode 100644 test/test_image_offset.py delete mode 100644 test/test_image_putalpha.py delete mode 100644 test/test_image_putdata.py delete mode 100644 test/test_image_putpalette.py delete mode 100644 test/test_image_quantize.py delete mode 100644 test/test_image_resize.py delete mode 100644 test/test_image_rotate.py delete mode 100644 test/test_image_thumbnail.py delete mode 100644 test/test_image_tobitmap.py delete mode 100644 test/test_image_tobytes.py delete mode 100644 test/test_image_transform.py delete mode 100644 test/test_imagechops.py delete mode 100644 test/test_imagecms.py delete mode 100644 test/test_imagecolor.py delete mode 100644 test/test_imagedraw.py delete mode 100644 test/test_imageenhance.py delete mode 100644 test/test_imagefileio.py delete mode 100644 test/test_imagefilter.py delete mode 100644 test/test_imagefont.py delete mode 100644 test/test_imagegrab.py delete mode 100644 test/test_imagemath.py delete mode 100644 test/test_imagemode.py delete mode 100644 test/test_imageops.py delete mode 100644 test/test_imageshow.py delete mode 100644 test/test_imagestat.py delete mode 100644 test/test_imagetk.py delete mode 100644 test/test_imagetransform.py delete mode 100644 test/test_imagewin.py delete mode 100644 test/test_lib_image.py delete mode 100644 test/test_lib_pack.py delete mode 100644 test/test_locale.py diff --git a/.travis.yml b/.travis.yml index ef5094a68..1d313a088 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,7 @@ python: install: - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" - "pip install cffi" - - "pip install coveralls nose" - - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then pip install unittest2; fi + - "pip install coveralls" # webp - pushd depends && ./install_webp.sh && popd @@ -29,23 +28,18 @@ script: - python setup.py build_ext --inplace # Don't cover PyPy: it fails intermittently and is x5.8 slower (#640) - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time python selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests test/; fi - - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time python Tests/run.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi # Cover the others - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose test/; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time python Tests/run.py --coverage; fi - + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi after_success: - coverage report - coveralls - pip install pep8 pyflakes - pep8 --statistics --count PIL/*.py - - pep8 --statistics --count test/*.py - - pep8 --statistics --count Tests/*.py + - pep8 --statistics --count Tests/*.py - pyflakes PIL/*.py | tee >(wc -l) - - pyflakes test/*.py | tee >(wc -l) - pyflakes Tests/*.py | tee >(wc -l) diff --git a/PIL/tests.py b/PIL/tests.py new file mode 100644 index 000000000..eb4a8342d --- /dev/null +++ b/PIL/tests.py @@ -0,0 +1,17 @@ +import unittest + + +class PillowTests(unittest.TestCase): + """ + Can we start moving the test suite here? + """ + + def test_suite_should_move_here(self): + """ + Great idea! + """ + assert True is True + + +if __name__ == '__main__': + unittest.main() diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py new file mode 100644 index 000000000..a30786458 --- /dev/null +++ b/Tests/test_000_sanity.py @@ -0,0 +1,24 @@ +from __future__ import print_function +from tester import * + +import PIL +import PIL.Image + +# Make sure we have the binary extension +im = PIL.Image.core.new("L", (100, 100)) + +assert PIL.Image.VERSION[:3] == '1.1' + +# Create an image and do stuff with it. +im = PIL.Image.new("1", (100, 100)) +assert (im.mode, im.size) == ('1', (100, 100)) +assert len(im.tobytes()) == 1300 + +# Create images in all remaining major modes. +im = PIL.Image.new("L", (100, 100)) +im = PIL.Image.new("P", (100, 100)) +im = PIL.Image.new("RGB", (100, 100)) +im = PIL.Image.new("I", (100, 100)) +im = PIL.Image.new("F", (100, 100)) + +print("ok") diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py new file mode 100644 index 000000000..99818229f --- /dev/null +++ b/Tests/test_bmp_reference.py @@ -0,0 +1,86 @@ +from tester import * + +from PIL import Image +import os + +base = os.path.join('Tests', 'images', 'bmp') + + +def get_files(d, ext='.bmp'): + return [os.path.join(base,d,f) for f + in os.listdir(os.path.join(base, d)) if ext in f] + +def test_bad(): + """ These shouldn't crash/dos, but they shouldn't return anything either """ + for f in get_files('b'): + try: + im = Image.open(f) + im.load() + except Exception as msg: + pass + # print ("Bad Image %s: %s" %(f,msg)) + +def test_questionable(): + """ These shouldn't crash/dos, but its not well defined that these are in spec """ + for f in get_files('q'): + try: + im = Image.open(f) + im.load() + except Exception as msg: + pass + # print ("Bad Image %s: %s" %(f,msg)) + + +def test_good(): + """ These should all work. There's a set of target files in the + html directory that we can compare against. """ + + # Target files, if they're not just replacing the extension + file_map = { 'pal1wb.bmp': 'pal1.png', + 'pal4rle.bmp': 'pal4.png', + 'pal8-0.bmp': 'pal8.png', + 'pal8rle.bmp': 'pal8.png', + 'pal8topdown.bmp': 'pal8.png', + 'pal8nonsquare.bmp': 'pal8nonsquare-v.png', + 'pal8os2.bmp': 'pal8.png', + 'pal8os2sp.bmp': 'pal8.png', + 'pal8os2v2.bmp': 'pal8.png', + 'pal8os2v2-16.bmp': 'pal8.png', + 'pal8v4.bmp': 'pal8.png', + 'pal8v5.bmp': 'pal8.png', + 'rgb16-565pal.bmp': 'rgb16-565.png', + 'rgb24pal.bmp': 'rgb24.png', + 'rgb32.bmp': 'rgb24.png', + 'rgb32bf.bmp': 'rgb24.png' + } + + def get_compare(f): + (head, name) = os.path.split(f) + if name in file_map: + return os.path.join(base, 'html', file_map[name]) + (name,ext) = os.path.splitext(name) + return os.path.join(base, 'html', "%s.png"%name) + + for f in get_files('g'): + try: + im = Image.open(f) + im.load() + compare = Image.open(get_compare(f)) + compare.load() + if im.mode == 'P': + # assert image similar doesn't really work + # with paletized image, since the palette might + # be differently ordered for an equivalent image. + im = im.convert('RGBA') + compare = im.convert('RGBA') + assert_image_similar(im, compare,5) + + + except Exception as msg: + # there are three here that are unsupported: + unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), + os.path.join(base, 'g', 'pal8rle.bmp'), + os.path.join(base, 'g', 'pal4rle.bmp')) + if f not in unsupported: + assert_true(False, "Unsupported Image %s: %s" %(f,msg)) + diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py new file mode 100644 index 000000000..4e06a732e --- /dev/null +++ b/Tests/test_file_fli.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.fli" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "FLI") diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py new file mode 100644 index 000000000..3e31f8879 --- /dev/null +++ b/Tests/test_file_icns.py @@ -0,0 +1,66 @@ +from tester import * + +from PIL import Image + +# sample icon file +file = "Images/pillow.icns" +data = open(file, "rb").read() + +enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') + +def test_sanity(): + # Loading this icon by default should result in the largest size + # (512x512@2x) being loaded + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGBA") + assert_equal(im.size, (1024, 1024)) + assert_equal(im.format, "ICNS") + +def test_sizes(): + # Check that we can load all of the sizes, and that the final pixel + # dimensions are as expected + im = Image.open(file) + for w,h,r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open(file) + im2.size = (w, h, r) + im2.load() + assert_equal(im2.mode, 'RGBA') + assert_equal(im2.size, (wr, hr)) + +def test_older_icon(): + # This icon was made with Icon Composer rather than iconutil; it still + # uses PNG rather than JP2, however (since it was made on 10.9). + im = Image.open('Tests/images/pillow2.icns') + for w,h,r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open('Tests/images/pillow2.icns') + im2.size = (w, h, r) + im2.load() + assert_equal(im2.mode, 'RGBA') + assert_equal(im2.size, (wr, hr)) + +def test_jp2_icon(): + # This icon was made by using Uli Kusterer's oldiconutil to replace + # the PNG images with JPEG 2000 ones. The advantage of doing this is + # that OS X 10.5 supports JPEG 2000 but not PNG; some commercial + # software therefore does just this. + + # (oldiconutil is here: https://github.com/uliwitness/oldiconutil) + + if not enable_jpeg2k: + return + + im = Image.open('Tests/images/pillow3.icns') + for w,h,r in im.info['sizes']: + wr = w * r + hr = h * r + im2 = Image.open('Tests/images/pillow3.icns') + im2.size = (w, h, r) + im2.load() + assert_equal(im2.mode, 'RGBA') + assert_equal(im2.size, (wr, hr)) + diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py new file mode 100644 index 000000000..e0db34acc --- /dev/null +++ b/Tests/test_file_ico.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.ico" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGBA") + assert_equal(im.size, (16, 16)) + assert_equal(im.format, "ICO") diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py new file mode 100644 index 000000000..ef2d40594 --- /dev/null +++ b/Tests/test_file_psd.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.psd" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PSD") diff --git a/test/test_file_xbm.py b/Tests/test_file_xbm.py similarity index 72% rename from test/test_file_xbm.py rename to Tests/test_file_xbm.py index 02aec70b1..f27a3a349 100644 --- a/test/test_file_xbm.py +++ b/Tests/test_file_xbm.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase +from tester import * from PIL import Image @@ -25,20 +25,10 @@ static char basic_bits[] = { }; """ +def test_pil151(): -class TestFileXbm(PillowTestCase): + im = Image.open(BytesIO(PIL151)) - def test_pil151(self): - from io import BytesIO - - im = Image.open(BytesIO(PIL151)) - - im.load() - self.assertEqual(im.mode, '1') - self.assertEqual(im.size, (32, 32)) - - -if __name__ == '__main__': - unittest.main() - -# End of file + assert_no_exception(lambda: im.load()) + assert_equal(im.mode, '1') + assert_equal(im.size, (32, 32)) diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py new file mode 100644 index 000000000..44135d028 --- /dev/null +++ b/Tests/test_file_xpm.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.xpm" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "XPM") diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py new file mode 100644 index 000000000..366bb4468 --- /dev/null +++ b/Tests/test_font_bdf.py @@ -0,0 +1,13 @@ +from tester import * + +from PIL import Image, FontFile, BdfFontFile + +filename = "Images/courB08.bdf" + +def test_sanity(): + + file = open(filename, "rb") + font = BdfFontFile.BdfFontFile(file) + + assert_true(isinstance(font, FontFile.FontFile)) + assert_equal(len([_f for _f in font.glyph if _f]), 190) diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py new file mode 100644 index 000000000..371b06a0b --- /dev/null +++ b/Tests/test_format_lab.py @@ -0,0 +1,41 @@ +from tester import * + +from PIL import Image + +def test_white(): + i = Image.open('Tests/images/lab.tif') + + bits = i.load() + + assert_equal(i.mode, 'LAB') + + assert_equal(i.getbands(), ('L','A', 'B')) + + k = i.getpixel((0,0)) + assert_equal(k, (255,128,128)) + + L = i.getdata(0) + a = i.getdata(1) + b = i.getdata(2) + + assert_equal(list(L), [255]*100) + assert_equal(list(a), [128]*100) + assert_equal(list(b), [128]*100) + + +def test_green(): + # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS + # == RGB: 0, 152, 117 + i = Image.open('Tests/images/lab-green.tif') + + k = i.getpixel((0,0)) + assert_equal(k, (128,28,128)) + + +def test_red(): + # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS + # == RGB: 255, 0, 124 + i = Image.open('Tests/images/lab-red.tif') + + k = i.getpixel((0,0)) + assert_equal(k, (128,228,128)) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py new file mode 100644 index 000000000..351621d3a --- /dev/null +++ b/Tests/test_image_array.py @@ -0,0 +1,33 @@ +from tester import * + +from PIL import Image + +im = lena().resize((128, 100)) + +def test_toarray(): + def test(mode): + ai = im.convert(mode).__array_interface__ + return ai["shape"], ai["typestr"], len(ai["data"]) + # assert_equal(test("1"), ((100, 128), '|b1', 1600)) + assert_equal(test("L"), ((100, 128), '|u1', 12800)) + assert_equal(test("I"), ((100, 128), Image._ENDIAN + 'i4', 51200)) # FIXME: wrong? + assert_equal(test("F"), ((100, 128), Image._ENDIAN + 'f4', 51200)) # FIXME: wrong? + assert_equal(test("RGB"), ((100, 128, 3), '|u1', 38400)) + assert_equal(test("RGBA"), ((100, 128, 4), '|u1', 51200)) + assert_equal(test("RGBX"), ((100, 128, 4), '|u1', 51200)) + +def test_fromarray(): + def test(mode): + i = im.convert(mode) + a = i.__array_interface__ + a["strides"] = 1 # pretend it's non-contigous + i.__array_interface__ = a # patch in new version of attribute + out = Image.fromarray(i) + return out.mode, out.size, list(i.getdata()) == list(out.getdata()) + # assert_equal(test("1"), ("1", (128, 100), True)) + assert_equal(test("L"), ("L", (128, 100), True)) + assert_equal(test("I"), ("I", (128, 100), True)) + assert_equal(test("F"), ("F", (128, 100), True)) + assert_equal(test("RGB"), ("RGB", (128, 100), True)) + assert_equal(test("RGBA"), ("RGBA", (128, 100), True)) + assert_equal(test("RGBX"), ("RGBA", (128, 100), True)) diff --git a/Tests/test_image_copy.py b/Tests/test_image_copy.py new file mode 100644 index 000000000..40a3dc496 --- /dev/null +++ b/Tests/test_image_copy.py @@ -0,0 +1,12 @@ +from tester import * + +from PIL import Image + +def test_copy(): + def copy(mode): + im = lena(mode) + out = im.copy() + assert_equal(out.mode, mode) + assert_equal(out.size, im.size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(copy, mode) diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py new file mode 100644 index 000000000..973606309 --- /dev/null +++ b/Tests/test_image_crop.py @@ -0,0 +1,52 @@ +from tester import * + +from PIL import Image + +def test_crop(): + def crop(mode): + out = lena(mode).crop((50, 50, 100, 100)) + assert_equal(out.mode, mode) + assert_equal(out.size, (50, 50)) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(crop, mode) + +def test_wide_crop(): + + def crop(*bbox): + i = im.crop(bbox) + h = i.histogram() + while h and not h[-1]: + del h[-1] + return tuple(h) + + im = Image.new("L", (100, 100), 1) + + assert_equal(crop(0, 0, 100, 100), (0, 10000)) + assert_equal(crop(25, 25, 75, 75), (0, 2500)) + + # sides + assert_equal(crop(-25, 0, 25, 50), (1250, 1250)) + assert_equal(crop(0, -25, 50, 25), (1250, 1250)) + assert_equal(crop(75, 0, 125, 50), (1250, 1250)) + assert_equal(crop(0, 75, 50, 125), (1250, 1250)) + + assert_equal(crop(-25, 25, 125, 75), (2500, 5000)) + assert_equal(crop(25, -25, 75, 125), (2500, 5000)) + + # corners + assert_equal(crop(-25, -25, 25, 25), (1875, 625)) + assert_equal(crop(75, -25, 125, 25), (1875, 625)) + assert_equal(crop(75, 75, 125, 125), (1875, 625)) + assert_equal(crop(-25, 75, 25, 125), (1875, 625)) + +# -------------------------------------------------------------------- + +def test_negative_crop(): + # Check negative crop size (@PIL171) + + im = Image.new("L", (512, 512)) + im = im.crop((400, 400, 200, 200)) + + assert_equal(im.size, (0, 0)) + assert_equal(len(im.getdata()), 0) + assert_exception(IndexError, lambda: im.getdata()[0]) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py new file mode 100644 index 000000000..f61e0c954 --- /dev/null +++ b/Tests/test_image_filter.py @@ -0,0 +1,82 @@ +from tester import * + +from PIL import Image +from PIL import ImageFilter + +def test_sanity(): + + def filter(filter): + im = lena("L") + out = im.filter(filter) + assert_equal(out.mode, im.mode) + assert_equal(out.size, im.size) + + filter(ImageFilter.BLUR) + filter(ImageFilter.CONTOUR) + filter(ImageFilter.DETAIL) + filter(ImageFilter.EDGE_ENHANCE) + filter(ImageFilter.EDGE_ENHANCE_MORE) + filter(ImageFilter.EMBOSS) + filter(ImageFilter.FIND_EDGES) + filter(ImageFilter.SMOOTH) + filter(ImageFilter.SMOOTH_MORE) + filter(ImageFilter.SHARPEN) + filter(ImageFilter.MaxFilter) + filter(ImageFilter.MedianFilter) + filter(ImageFilter.MinFilter) + filter(ImageFilter.ModeFilter) + filter(ImageFilter.Kernel((3, 3), list(range(9)))) + + assert_exception(TypeError, lambda: filter("hello")) + +def test_crash(): + + # crashes on small images + im = Image.new("RGB", (1, 1)) + assert_no_exception(lambda: im.filter(ImageFilter.SMOOTH)) + + im = Image.new("RGB", (2, 2)) + assert_no_exception(lambda: im.filter(ImageFilter.SMOOTH)) + + im = Image.new("RGB", (3, 3)) + assert_no_exception(lambda: im.filter(ImageFilter.SMOOTH)) + +def test_modefilter(): + + def modefilter(mode): + im = Image.new(mode, (3, 3), None) + im.putdata(list(range(9))) + # image is: + # 0 1 2 + # 3 4 5 + # 6 7 8 + mod = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) + im.putdata([0, 0, 1, 2, 5, 1, 5, 2, 0]) # mode=0 + mod2 = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) + return mod, mod2 + + assert_equal(modefilter("1"), (4, 0)) + assert_equal(modefilter("L"), (4, 0)) + assert_equal(modefilter("P"), (4, 0)) + assert_equal(modefilter("RGB"), ((4, 0, 0), (0, 0, 0))) + +def test_rankfilter(): + + def rankfilter(mode): + im = Image.new(mode, (3, 3), None) + im.putdata(list(range(9))) + # image is: + # 0 1 2 + # 3 4 5 + # 6 7 8 + min = im.filter(ImageFilter.MinFilter).getpixel((1, 1)) + med = im.filter(ImageFilter.MedianFilter).getpixel((1, 1)) + max = im.filter(ImageFilter.MaxFilter).getpixel((1, 1)) + return min, med, max + + assert_equal(rankfilter("1"), (0, 4, 8)) + assert_equal(rankfilter("L"), (0, 4, 8)) + assert_exception(ValueError, lambda: rankfilter("P")) + assert_equal(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0))) + assert_equal(rankfilter("I"), (0, 4, 8)) + assert_equal(rankfilter("F"), (0.0, 4.0, 8.0)) diff --git a/Tests/test_image_frombytes.py b/Tests/test_image_frombytes.py new file mode 100644 index 000000000..aa157aa6a --- /dev/null +++ b/Tests/test_image_frombytes.py @@ -0,0 +1,10 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + im1 = lena() + im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes()) + + assert_image_equal(im1, im2) + diff --git a/Tests/test_image_getbands.py b/Tests/test_image_getbands.py new file mode 100644 index 000000000..e7f1ec5a9 --- /dev/null +++ b/Tests/test_image_getbands.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_getbands(): + + assert_equal(Image.new("1", (1, 1)).getbands(), ("1",)) + assert_equal(Image.new("L", (1, 1)).getbands(), ("L",)) + assert_equal(Image.new("I", (1, 1)).getbands(), ("I",)) + assert_equal(Image.new("F", (1, 1)).getbands(), ("F",)) + assert_equal(Image.new("P", (1, 1)).getbands(), ("P",)) + assert_equal(Image.new("RGB", (1, 1)).getbands(), ("R", "G", "B")) + assert_equal(Image.new("RGBA", (1, 1)).getbands(), ("R", "G", "B", "A")) + assert_equal(Image.new("CMYK", (1, 1)).getbands(), ("C", "M", "Y", "K")) + assert_equal(Image.new("YCbCr", (1, 1)).getbands(), ("Y", "Cb", "Cr")) diff --git a/Tests/test_image_getbbox.py b/Tests/test_image_getbbox.py new file mode 100644 index 000000000..c0f846169 --- /dev/null +++ b/Tests/test_image_getbbox.py @@ -0,0 +1,36 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + bbox = lena().getbbox() + assert_true(isinstance(bbox, tuple)) + +def test_bbox(): + + # 8-bit mode + im = Image.new("L", (100, 100), 0) + assert_equal(im.getbbox(), None) + + im.paste(255, (10, 25, 90, 75)) + assert_equal(im.getbbox(), (10, 25, 90, 75)) + + im.paste(255, (25, 10, 75, 90)) + assert_equal(im.getbbox(), (10, 10, 90, 90)) + + im.paste(255, (-10, -10, 110, 110)) + assert_equal(im.getbbox(), (0, 0, 100, 100)) + + # 32-bit mode + im = Image.new("RGB", (100, 100), 0) + assert_equal(im.getbbox(), None) + + im.paste(255, (10, 25, 90, 75)) + assert_equal(im.getbbox(), (10, 25, 90, 75)) + + im.paste(255, (25, 10, 75, 90)) + assert_equal(im.getbbox(), (10, 10, 90, 90)) + + im.paste(255, (-10, -10, 110, 110)) + assert_equal(im.getbbox(), (0, 0, 100, 100)) diff --git a/Tests/test_image_getcolors.py b/Tests/test_image_getcolors.py new file mode 100644 index 000000000..2429f9350 --- /dev/null +++ b/Tests/test_image_getcolors.py @@ -0,0 +1,64 @@ +from tester import * + +from PIL import Image + +def test_getcolors(): + + def getcolors(mode, limit=None): + im = lena(mode) + if limit: + colors = im.getcolors(limit) + else: + colors = im.getcolors() + if colors: + return len(colors) + return None + + assert_equal(getcolors("1"), 2) + assert_equal(getcolors("L"), 193) + assert_equal(getcolors("I"), 193) + assert_equal(getcolors("F"), 193) + assert_equal(getcolors("P"), 54) # fixed palette + assert_equal(getcolors("RGB"), None) + assert_equal(getcolors("RGBA"), None) + assert_equal(getcolors("CMYK"), None) + assert_equal(getcolors("YCbCr"), None) + + assert_equal(getcolors("L", 128), None) + assert_equal(getcolors("L", 1024), 193) + + assert_equal(getcolors("RGB", 8192), None) + assert_equal(getcolors("RGB", 16384), 14836) + assert_equal(getcolors("RGB", 100000), 14836) + + assert_equal(getcolors("RGBA", 16384), 14836) + assert_equal(getcolors("CMYK", 16384), 14836) + assert_equal(getcolors("YCbCr", 16384), 11995) + +# -------------------------------------------------------------------- + +def test_pack(): + # Pack problems for small tables (@PIL209) + + im = lena().quantize(3).convert("RGB") + + expected = [(3236, (227, 183, 147)), (6297, (143, 84, 81)), (6851, (208, 143, 112))] + + A = im.getcolors(maxcolors=2) + assert_equal(A, None) + + A = im.getcolors(maxcolors=3) + A.sort() + assert_equal(A, expected) + + A = im.getcolors(maxcolors=4) + A.sort() + assert_equal(A, expected) + + A = im.getcolors(maxcolors=8) + A.sort() + assert_equal(A, expected) + + A = im.getcolors(maxcolors=16) + A.sort() + assert_equal(A, expected) diff --git a/Tests/test_image_getextrema.py b/Tests/test_image_getextrema.py new file mode 100644 index 000000000..86106cde0 --- /dev/null +++ b/Tests/test_image_getextrema.py @@ -0,0 +1,17 @@ +from tester import * + +from PIL import Image + +def test_extrema(): + + def extrema(mode): + return lena(mode).getextrema() + + assert_equal(extrema("1"), (0, 255)) + assert_equal(extrema("L"), (40, 235)) + assert_equal(extrema("I"), (40, 235)) + assert_equal(extrema("F"), (40.0, 235.0)) + assert_equal(extrema("P"), (11, 218)) # fixed palette + assert_equal(extrema("RGB"), ((61, 255), (26, 234), (44, 223))) + assert_equal(extrema("RGBA"), ((61, 255), (26, 234), (44, 223), (255, 255))) + assert_equal(extrema("CMYK"), ((0, 194), (21, 229), (32, 211), (0, 0))) diff --git a/Tests/test_image_getim.py b/Tests/test_image_getim.py new file mode 100644 index 000000000..8d2f12fc2 --- /dev/null +++ b/Tests/test_image_getim.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + type_repr = repr(type(im.getim())) + + if py3: + assert_true("PyCapsule" in type_repr) + + assert_true(isinstance(im.im.id, int)) + diff --git a/Tests/test_image_getpalette.py b/Tests/test_image_getpalette.py new file mode 100644 index 000000000..5dc923b9f --- /dev/null +++ b/Tests/test_image_getpalette.py @@ -0,0 +1,19 @@ +from tester import * + +from PIL import Image + +def test_palette(): + def palette(mode): + p = lena(mode).getpalette() + if p: + return p[:10] + return None + assert_equal(palette("1"), None) + assert_equal(palette("L"), None) + assert_equal(palette("I"), None) + assert_equal(palette("F"), None) + assert_equal(palette("P"), [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + assert_equal(palette("RGB"), None) + assert_equal(palette("RGBA"), None) + assert_equal(palette("CMYK"), None) + assert_equal(palette("YCbCr"), None) diff --git a/Tests/test_image_histogram.py b/Tests/test_image_histogram.py new file mode 100644 index 000000000..c86cb578a --- /dev/null +++ b/Tests/test_image_histogram.py @@ -0,0 +1,19 @@ +from tester import * + +from PIL import Image + +def test_histogram(): + + def histogram(mode): + h = lena(mode).histogram() + return len(h), min(h), max(h) + + assert_equal(histogram("1"), (256, 0, 8872)) + assert_equal(histogram("L"), (256, 0, 199)) + assert_equal(histogram("I"), (256, 0, 199)) + assert_equal(histogram("F"), (256, 0, 199)) + assert_equal(histogram("P"), (256, 0, 2912)) + assert_equal(histogram("RGB"), (768, 0, 285)) + assert_equal(histogram("RGBA"), (1024, 0, 16384)) + assert_equal(histogram("CMYK"), (1024, 0, 16384)) + assert_equal(histogram("YCbCr"), (768, 0, 741)) diff --git a/Tests/test_image_load.py b/Tests/test_image_load.py new file mode 100644 index 000000000..b385b9686 --- /dev/null +++ b/Tests/test_image_load.py @@ -0,0 +1,27 @@ +from tester import * + +from PIL import Image + +import os + +def test_sanity(): + + im = lena() + + pix = im.load() + + assert_equal(pix[0, 0], (223, 162, 133)) + +def test_close(): + im = Image.open("Images/lena.gif") + assert_no_exception(lambda: im.close()) + assert_exception(ValueError, lambda: im.load()) + assert_exception(ValueError, lambda: im.getpixel((0,0))) + +def test_contextmanager(): + fn = None + with Image.open("Images/lena.gif") as im: + fn = im.fp.fileno() + assert_no_exception(lambda: os.fstat(fn)) + + assert_exception(OSError, lambda: os.fstat(fn)) diff --git a/Tests/test_image_mode.py b/Tests/test_image_mode.py new file mode 100644 index 000000000..cd5bd47f5 --- /dev/null +++ b/Tests/test_image_mode.py @@ -0,0 +1,27 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + assert_no_exception(lambda: im.mode) + +def test_properties(): + def check(mode, *result): + signature = ( + Image.getmodebase(mode), Image.getmodetype(mode), + Image.getmodebands(mode), Image.getmodebandnames(mode), + ) + assert_equal(signature, result) + check("1", "L", "L", 1, ("1",)) + check("L", "L", "L", 1, ("L",)) + check("P", "RGB", "L", 1, ("P",)) + check("I", "L", "I", 1, ("I",)) + check("F", "L", "F", 1, ("F",)) + check("RGB", "RGB", "L", 3, ("R", "G", "B")) + check("RGBA", "RGB", "L", 4, ("R", "G", "B", "A")) + check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) + check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) + check("CMYK", "RGB", "L", 4, ("C", "M", "Y", "K")) + check("YCbCr", "RGB", "L", 3, ("Y", "Cb", "Cr")) diff --git a/Tests/test_image_offset.py b/Tests/test_image_offset.py new file mode 100644 index 000000000..f6356907a --- /dev/null +++ b/Tests/test_image_offset.py @@ -0,0 +1,16 @@ +from tester import * + +from PIL import Image + +def test_offset(): + + im1 = lena() + + im2 = assert_warning(DeprecationWarning, lambda: im1.offset(10)) + assert_equal(im1.getpixel((0, 0)), im2.getpixel((10, 10))) + + im2 = assert_warning(DeprecationWarning, lambda: im1.offset(10, 20)) + assert_equal(im1.getpixel((0, 0)), im2.getpixel((10, 20))) + + im2 = assert_warning(DeprecationWarning, lambda: im1.offset(20, 20)) + assert_equal(im1.getpixel((0, 0)), im2.getpixel((20, 20))) diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_paste.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_putalpha.py b/Tests/test_image_putalpha.py new file mode 100644 index 000000000..b23f69834 --- /dev/null +++ b/Tests/test_image_putalpha.py @@ -0,0 +1,43 @@ +from tester import * + +from PIL import Image + +def test_interface(): + + im = Image.new("RGBA", (1, 1), (1, 2, 3, 0)) + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 0)) + + im = Image.new("RGBA", (1, 1), (1, 2, 3)) + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 255)) + + im.putalpha(Image.new("L", im.size, 4)) + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) + + im.putalpha(5) + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 5)) + +def test_promote(): + + im = Image.new("L", (1, 1), 1) + assert_equal(im.getpixel((0, 0)), 1) + + im.putalpha(2) + assert_equal(im.mode, 'LA') + assert_equal(im.getpixel((0, 0)), (1, 2)) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + assert_equal(im.getpixel((0, 0)), (1, 2, 3)) + + im.putalpha(4) + assert_equal(im.mode, 'RGBA') + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) + +def test_readonly(): + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.readonly = 1 + + im.putalpha(4) + assert_false(im.readonly) + assert_equal(im.mode, 'RGBA') + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py new file mode 100644 index 000000000..e25359fdf --- /dev/null +++ b/Tests/test_image_putdata.py @@ -0,0 +1,40 @@ +from tester import * + +import sys + +from PIL import Image + +def test_sanity(): + + im1 = lena() + + data = list(im1.getdata()) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.putdata(data) + + assert_image_equal(im1, im2) + + # readonly + im2 = Image.new(im1.mode, im2.size, 0) + im2.readonly = 1 + im2.putdata(data) + + assert_false(im2.readonly) + assert_image_equal(im1, im2) + + +def test_long_integers(): + # see bug-200802-systemerror + def put(value): + im = Image.new("RGBA", (1, 1)) + im.putdata([value]) + return im.getpixel((0, 0)) + assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255)) + assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255)) + assert_equal(put(-1), (255, 255, 255, 255)) + assert_equal(put(-1), (255, 255, 255, 255)) + if sys.maxsize > 2**32: + assert_equal(put(sys.maxsize), (255, 255, 255, 255)) + else: + assert_equal(put(sys.maxsize), (255, 255, 255, 127)) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py new file mode 100644 index 000000000..b7ebb8853 --- /dev/null +++ b/Tests/test_image_putpalette.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image +from PIL import ImagePalette + +def test_putpalette(): + def palette(mode): + im = lena(mode).copy() + im.putpalette(list(range(256))*3) + p = im.getpalette() + if p: + return im.mode, p[:10] + return im.mode + assert_exception(ValueError, lambda: palette("1")) + assert_equal(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + assert_equal(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + assert_exception(ValueError, lambda: palette("I")) + assert_exception(ValueError, lambda: palette("F")) + assert_exception(ValueError, lambda: palette("RGB")) + assert_exception(ValueError, lambda: palette("RGBA")) + assert_exception(ValueError, lambda: palette("YCbCr")) + +def test_imagepalette(): + im = lena("P") + assert_no_exception(lambda: im.putpalette(ImagePalette.negative())) + assert_no_exception(lambda: im.putpalette(ImagePalette.random())) + assert_no_exception(lambda: im.putpalette(ImagePalette.sepia())) + assert_no_exception(lambda: im.putpalette(ImagePalette.wedge())) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py new file mode 100644 index 000000000..dbf68a25e --- /dev/null +++ b/Tests/test_image_quantize.py @@ -0,0 +1,27 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + + im = im.quantize() + assert_image(im, "P", im.size) + + im = lena() + im = im.quantize(palette=lena("P")) + assert_image(im, "P", im.size) + +def test_octree_quantize(): + im = lena() + + im = im.quantize(100, Image.FASTOCTREE) + assert_image(im, "P", im.size) + + assert len(im.getcolors()) == 100 + +def test_rgba_quantize(): + im = lena('RGBA') + assert_no_exception(lambda: im.quantize()) + assert_exception(Exception, lambda: im.quantize(method=0)) diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py new file mode 100644 index 000000000..4e228a396 --- /dev/null +++ b/Tests/test_image_resize.py @@ -0,0 +1,12 @@ +from tester import * + +from PIL import Image + +def test_resize(): + def resize(mode, size): + out = lena(mode).resize(size) + assert_equal(out.mode, mode) + assert_equal(out.size, size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(resize, mode, (100, 100)) + yield_test(resize, mode, (200, 200)) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py new file mode 100644 index 000000000..5e4782c87 --- /dev/null +++ b/Tests/test_image_rotate.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_rotate(): + def rotate(mode): + im = lena(mode) + out = im.rotate(45) + assert_equal(out.mode, mode) + assert_equal(out.size, im.size) # default rotate clips output + out = im.rotate(45, expand=1) + assert_equal(out.mode, mode) + assert_true(out.size != im.size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(rotate, mode) diff --git a/Tests/test_image_save.py b/Tests/test_image_save.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_save.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_seek.py b/Tests/test_image_seek.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_seek.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_show.py b/Tests/test_image_show.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_show.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_tell.py b/Tests/test_image_tell.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_tell.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py new file mode 100644 index 000000000..871dd1f54 --- /dev/null +++ b/Tests/test_image_thumbnail.py @@ -0,0 +1,36 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + im.thumbnail((100, 100)) + + assert_image(im, im.mode, (100, 100)) + +def test_aspect(): + + im = lena() + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (50, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((50, 100)) + assert_image(im, im.mode, (50, 100)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 50)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 50)) + assert_image(im, im.mode, (100, 50)) + + im = lena().resize((128, 128)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 100)) diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py new file mode 100644 index 000000000..6fb10dd53 --- /dev/null +++ b/Tests/test_image_tobitmap.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + assert_exception(ValueError, lambda: lena().tobitmap()) + assert_no_exception(lambda: lena().convert("1").tobitmap()) + + im1 = lena().convert("1") + + bitmap = im1.tobitmap() + + assert_true(isinstance(bitmap, bytes)) + assert_image_equal(im1, fromstring(bitmap)) diff --git a/Tests/test_image_tobytes.py b/Tests/test_image_tobytes.py new file mode 100644 index 000000000..d42399993 --- /dev/null +++ b/Tests/test_image_tobytes.py @@ -0,0 +1,7 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + data = lena().tobytes() + assert_true(isinstance(data, bytes)) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py new file mode 100644 index 000000000..dd9b6fe5c --- /dev/null +++ b/Tests/test_image_transform.py @@ -0,0 +1,116 @@ +from tester import * + +from PIL import Image + +def test_extent(): + im = lena('RGB') + (w,h) = im.size + transformed = im.transform(im.size, Image.EXTENT, + (0,0, + w//2,h//2), # ul -> lr + Image.BILINEAR) + + + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) + + assert_image_similar(transformed, scaled, 10) # undone -- precision? + +def test_quad(): + # one simple quad transform, equivalent to scale & crop upper left quad + im = lena('RGB') + (w,h) = im.size + transformed = im.transform(im.size, Image.QUAD, + (0,0,0,h//2, + w//2,h//2,w//2,0), # ul -> ccw around quad + Image.BILINEAR) + + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) + + assert_image_equal(transformed, scaled) + +def test_mesh(): + # this should be a checkerboard of halfsized lenas in ul, lr + im = lena('RGBA') + (w,h) = im.size + transformed = im.transform(im.size, Image.MESH, + [((0,0,w//2,h//2), # box + (0,0,0,h, + w,h,w,0)), # ul -> ccw around quad + ((w//2,h//2,w,h), # box + (0,0,0,h, + w,h,w,0))], # ul -> ccw around quad + Image.BILINEAR) + + #transformed.save('transformed.png') + + scaled = im.resize((w//2, h//2), Image.BILINEAR) + + checker = Image.new('RGBA', im.size) + checker.paste(scaled, (0,0)) + checker.paste(scaled, (w//2,h//2)) + + assert_image_equal(transformed, checker) + + # now, check to see that the extra area is (0,0,0,0) + blank = Image.new('RGBA', (w//2,h//2), (0,0,0,0)) + + assert_image_equal(blank, transformed.crop((w//2,0,w,h//2))) + assert_image_equal(blank, transformed.crop((0,h//2,w//2,h))) + +def _test_alpha_premult(op): + # create image with half white, half black, with the black half transparent. + # do op, + # there should be no darkness in the white section. + im = Image.new('RGBA', (10,10), (0,0,0,0)); + im2 = Image.new('RGBA', (5,10), (255,255,255,255)); + im.paste(im2, (0,0)) + + im = op(im, (40,10)) + im_background = Image.new('RGB', (40,10), (255,255,255)) + im_background.paste(im, (0,0), im) + + hist = im_background.histogram() + assert_equal(40*10, hist[-1]) + + +def test_alpha_premult_resize(): + + def op (im, sz): + return im.resize(sz, Image.LINEAR) + + _test_alpha_premult(op) + +def test_alpha_premult_transform(): + + def op(im, sz): + (w,h) = im.size + return im.transform(sz, Image.EXTENT, + (0,0, + w,h), + Image.BILINEAR) + + _test_alpha_premult(op) + + +def test_blank_fill(): + # attempting to hit + # https://github.com/python-pillow/Pillow/issues/254 reported + # + # issue is that transforms with transparent overflow area + # contained junk from previous images, especially on systems with + # constrained memory. So, attempt to fill up memory with a + # pattern, free it, and then run the mesh test again. Using a 1Mp + # image with 4 bands, for 4 megs of data allocated, x 64. OMM (64 + # bit 12.04 VM with 512 megs available, this fails with Pillow < + # a0eaf06cc5f62a6fb6de556989ac1014ff3348ea + # + # Running by default, but I'd totally understand not doing it in + # the future + + foo = [Image.new('RGBA',(1024,1024), (a,a,a,a)) + for a in range(1,65)] + + # Yeah. Watch some JIT optimize this out. + foo = None + + test_mesh() diff --git a/Tests/test_image_verify.py b/Tests/test_image_verify.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_verify.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py new file mode 100644 index 000000000..16eaaf55e --- /dev/null +++ b/Tests/test_imagechops.py @@ -0,0 +1,56 @@ +from tester import * + +from PIL import Image +from PIL import ImageChops + +def test_sanity(): + + im = lena("L") + + ImageChops.constant(im, 128) + ImageChops.duplicate(im) + ImageChops.invert(im) + ImageChops.lighter(im, im) + ImageChops.darker(im, im) + ImageChops.difference(im, im) + ImageChops.multiply(im, im) + ImageChops.screen(im, im) + + ImageChops.add(im, im) + ImageChops.add(im, im, 2.0) + ImageChops.add(im, im, 2.0, 128) + ImageChops.subtract(im, im) + ImageChops.subtract(im, im, 2.0) + ImageChops.subtract(im, im, 2.0, 128) + + ImageChops.add_modulo(im, im) + ImageChops.subtract_modulo(im, im) + + ImageChops.blend(im, im, 0.5) + ImageChops.composite(im, im, im) + + ImageChops.offset(im, 10) + ImageChops.offset(im, 10, 20) + +def test_logical(): + + def table(op, a, b): + out = [] + for x in (a, b): + imx = Image.new("1", (1, 1), x) + for y in (a, b): + imy = Image.new("1", (1, 1), y) + out.append(op(imx, imy).getpixel((0, 0))) + return tuple(out) + + assert_equal(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) + + assert_equal(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + + assert_equal(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py new file mode 100644 index 000000000..f52101eb1 --- /dev/null +++ b/Tests/test_imagecms.py @@ -0,0 +1,203 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageCms + ImageCms.core.profile_open +except ImportError: + skip() + +SRGB = "Tests/icc/sRGB.icm" + + +def test_sanity(): + + # basic smoke test. + # this mostly follows the cms_test outline. + + v = ImageCms.versions() # should return four strings + assert_equal(v[0], '1.0.0 pil') + assert_equal(list(map(type, v)), [str, str, str, str]) + + # internal version number + assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") + + i = ImageCms.profileToProfile(lena(), SRGB, SRGB) + assert_image(i, "RGB", (128, 128)) + + i = lena() + ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + i = lena() + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + ImageCms.applyTransform(lena(), t, inPlace=True) + assert_image(i, "RGB", (128, 128)) + + p = ImageCms.createProfile("sRGB") + o = ImageCms.getOpenProfile(SRGB) + t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") + assert_equal(t.inputMode, "RGB") + assert_equal(t.outputMode, "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + # test PointTransform convenience API + lena().point(t) + + +def test_name(): + # get profile information for file + assert_equal(ImageCms.getProfileName(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + +def test_info(): + assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), + ['sRGB IEC61966-2.1', '', + 'Copyright (c) 1998 Hewlett-Packard Company', '']) + + +def test_copyright(): + assert_equal(ImageCms.getProfileCopyright(SRGB).strip(), + 'Copyright (c) 1998 Hewlett-Packard Company') + + +def test_manufacturer(): + assert_equal(ImageCms.getProfileManufacturer(SRGB).strip(), + 'IEC http://www.iec.ch') + + +def test_model(): + assert_equal(ImageCms.getProfileModel(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + +def test_description(): + assert_equal(ImageCms.getProfileDescription(SRGB).strip(), + 'sRGB IEC61966-2.1') + + +def test_intent(): + assert_equal(ImageCms.getDefaultIntent(SRGB), 0) + assert_equal(ImageCms.isIntentSupported( + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + +def test_profile_object(): + # same, using profile object + p = ImageCms.createProfile("sRGB") +# assert_equal(ImageCms.getProfileName(p).strip(), +# 'sRGB built-in - (lcms internal)') +# assert_equal(ImageCms.getProfileInfo(p).splitlines(), +# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) + assert_equal(ImageCms.getDefaultIntent(p), 0) + assert_equal(ImageCms.isIntentSupported( + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + + +def test_extensions(): + # extensions + i = Image.open("Tests/images/rgb.jpg") + p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) + assert_equal(ImageCms.getProfileName(p).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + + +def test_exceptions(): + # the procedural pyCMS API uses PyCMSError for all sorts of errors + assert_exception( + ImageCms.PyCMSError, + lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) + assert_exception( + ImageCms.PyCMSError, + lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) + assert_exception( + ImageCms.PyCMSError, + lambda: ImageCms.getProfileName(None)) + assert_exception( + ImageCms.PyCMSError, + lambda: ImageCms.isIntentSupported(SRGB, None, None)) + + +def test_display_profile(): + # try fetching the profile for the current display device + assert_no_exception(lambda: ImageCms.get_display_profile()) + + +def test_lab_color_profile(): + ImageCms.createProfile("LAB", 5000) + ImageCms.createProfile("LAB", 6500) + + +def test_simple_lab(): + i = Image.new('RGB', (10, 10), (128, 128, 128)) + + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + i_lab = ImageCms.applyTransform(i, t) + + assert_equal(i_lab.mode, 'LAB') + + k = i_lab.getpixel((0, 0)) + assert_equal(k, (137, 128, 128)) # not a linear luminance map. so L != 128 + + L = i_lab.getdata(0) + a = i_lab.getdata(1) + b = i_lab.getdata(2) + + assert_equal(list(L), [137] * 100) + assert_equal(list(a), [128] * 100) + assert_equal(list(b), [128] * 100) + + +def test_lab_color(): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + # Need to add a type mapping for some PIL type to TYPE_Lab_8 in + # findLCMSType, and have that mapping work back to a PIL mode (likely RGB). + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "LAB", (128, 128)) + + # i.save('temp.lab.tif') # visually verified vs PS. + + target = Image.open('Tests/images/lena.Lab.tif') + + assert_image_similar(i, target, 30) + + +def test_lab_srgb(): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + img = Image.open('Tests/images/lena.Lab.tif') + + img_srgb = ImageCms.applyTransform(img, t) + + # img_srgb.save('temp.srgb.tif') # visually verified vs ps. + + assert_image_similar(lena(), img_srgb, 30) + + +def test_lab_roundtrip(): + # check to see if we're at least internally consistent. + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + i = ImageCms.applyTransform(lena(), t) + out = ImageCms.applyTransform(i, t2) + + assert_image_similar(lena(), out, 2) diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py new file mode 100644 index 000000000..c67c20255 --- /dev/null +++ b/Tests/test_imagecolor.py @@ -0,0 +1,54 @@ +from tester import * + +from PIL import Image +from PIL import ImageColor + +# -------------------------------------------------------------------- +# sanity + +assert_equal((255, 0, 0), ImageColor.getrgb("#f00")) +assert_equal((255, 0, 0), ImageColor.getrgb("#ff0000")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) +assert_equal((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) +assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)")) +assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) +assert_equal((255, 0, 0), ImageColor.getrgb("red")) + +# -------------------------------------------------------------------- +# look for rounding errors (based on code by Tim Hatch) + +for color in list(ImageColor.colormap.keys()): + expected = Image.new("RGB", (1, 1), color).convert("L").getpixel((0, 0)) + actual = Image.new("L", (1, 1), color).getpixel((0, 0)) + assert_equal(expected, actual) + +assert_equal((0, 0, 0), ImageColor.getcolor("black", "RGB")) +assert_equal((255, 255, 255), ImageColor.getcolor("white", "RGB")) +assert_equal((0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB")) +Image.new("RGB", (1, 1), "white") + +assert_equal((0, 0, 0, 255), ImageColor.getcolor("black", "RGBA")) +assert_equal((255, 255, 255, 255), ImageColor.getcolor("white", "RGBA")) +assert_equal((0, 255, 115, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGBA")) +Image.new("RGBA", (1, 1), "white") + +assert_equal(0, ImageColor.getcolor("black", "L")) +assert_equal(255, ImageColor.getcolor("white", "L")) +assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) +Image.new("L", (1, 1), "white") + +assert_equal(0, ImageColor.getcolor("black", "1")) +assert_equal(255, ImageColor.getcolor("white", "1")) +# The following test is wrong, but is current behavior +# The correct result should be 255 due to the mode 1 +assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) +# Correct behavior +# assert_equal(255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) +Image.new("1", (1, 1), "white") + +assert_equal((0, 255), ImageColor.getcolor("black", "LA")) +assert_equal((255, 255), ImageColor.getcolor("white", "LA")) +assert_equal((162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) +Image.new("LA", (1, 1), "white") diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py new file mode 100644 index 000000000..7b682020e --- /dev/null +++ b/Tests/test_imagedraw.py @@ -0,0 +1,270 @@ +from tester import * + +from PIL import Image +from PIL import ImageColor +from PIL import ImageDraw + +# Image size +w, h = 100, 100 + +# Bounding box points +x0 = int(w / 4) +x1 = int(x0 * 3) +y0 = int(h / 4) +y1 = int(x0 * 3) + +# Two kinds of bounding box +bbox1 = [(x0, y0), (x1, y1)] +bbox2 = [x0, y0, x1, y1] + +# Two kinds of coordinate sequences +points1 = [(10, 10), (20, 40), (30, 30)] +points2 = [10, 10, 20, 40, 30, 30] + + +def test_sanity(): + + im = lena("RGB").copy() + + draw = ImageDraw.ImageDraw(im) + draw = ImageDraw.Draw(im) + + draw.ellipse(list(range(4))) + draw.line(list(range(10))) + draw.polygon(list(range(100))) + draw.rectangle(list(range(4))) + + success() + + +def test_deprecated(): + + im = lena().copy() + + draw = ImageDraw.Draw(im) + + assert_warning(DeprecationWarning, lambda: draw.setink(0)) + assert_warning(DeprecationWarning, lambda: draw.setfill(0)) + + +def helper_arc(bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + # FIXME Fill param should be named outline. + draw.arc(bbox, 0, 180) + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_arc.png")) + + +def test_arc1(): + helper_arc(bbox1) + + +def test_arc2(): + helper_arc(bbox2) + + +def test_bitmap(): + # Arrange + small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.bitmap((10, 10), small) + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png")) + + +def helper_chord(bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.chord(bbox, 0, 180, fill="red", outline="yellow") + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_chord.png")) + + +def test_chord1(): + helper_chord(bbox1) + + +def test_chord2(): + helper_chord(bbox2) + + +def helper_ellipse(bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.ellipse(bbox, fill="green", outline="blue") + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_ellipse.png")) + + +def test_ellipse1(): + helper_ellipse(bbox1) + + +def test_ellipse2(): + helper_ellipse(bbox2) + + +def helper_line(points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.line(points1, fill="yellow", width=2) + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) + + +def test_line1(): + helper_line(points1) + + +def test_line2(): + helper_line(points2) + + +def helper_pieslice(bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.pieslice(bbox, -90, 45, fill="white", outline="blue") + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_pieslice.png")) + + +def test_pieslice1(): + helper_pieslice(bbox1) + + +def test_pieslice2(): + helper_pieslice(bbox2) + + +def helper_point(points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.point(points1, fill="yellow") + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png")) + + +def test_point1(): + helper_point(points1) + + +def test_point2(): + helper_point(points2) + + +def helper_polygon(points): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.polygon(points1, fill="red", outline="blue") + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) + + +def test_polygon1(): + helper_polygon(points1) + + +def test_polygon2(): + helper_polygon(points2) + + +def helper_rectangle(bbox): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + + # Act + draw.rectangle(bbox, fill="black", outline="green") + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) + + +def test_rectangle1(): + helper_rectangle(bbox1) + + +def test_rectangle2(): + helper_rectangle(bbox2) + + +def test_floodfill(): + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + draw.rectangle(bbox2, outline="yellow", fill="green") + centre_point = (int(w/2), int(h/2)) + + # Act + ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill.png")) + + +def test_floodfill_border(): + # floodfill() is experimental + if hasattr(sys, 'pypy_version_info'): + # Causes fatal RPython error on PyPy + skip() + + # Arrange + im = Image.new("RGB", (w, h)) + draw = ImageDraw.Draw(im) + draw.rectangle(bbox2, outline="yellow", fill="green") + centre_point = (int(w/2), int(h/2)) + + # Act + ImageDraw.floodfill( + im, centre_point, ImageColor.getrgb("red"), + border=ImageColor.getrgb("black")) + del draw + + # Assert + assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) + + +# End of file diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py new file mode 100644 index 000000000..04f16bfa5 --- /dev/null +++ b/Tests/test_imageenhance.py @@ -0,0 +1,19 @@ +from tester import * + +from PIL import Image +from PIL import ImageEnhance + +def test_sanity(): + + # FIXME: assert_image + assert_no_exception(lambda: ImageEnhance.Color(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Contrast(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Brightness(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Sharpness(lena()).enhance(0.5)) + +def test_crash(): + + # crashes on small images + im = Image.new("RGB", (1, 1)) + assert_no_exception(lambda: ImageEnhance.Sharpness(im).enhance(0.5)) + diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py new file mode 100644 index 000000000..c63f07bb0 --- /dev/null +++ b/Tests/test_imagefileio.py @@ -0,0 +1,24 @@ +from tester import * + +from PIL import Image +from PIL import ImageFileIO + +def test_fileio(): + + class DumbFile: + def __init__(self, data): + self.data = data + def read(self, bytes=None): + assert_equal(bytes, None) + return self.data + def close(self): + pass + + im1 = lena() + + io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) + + im2 = Image.open(io) + assert_image_equal(im1, im2) + + diff --git a/Tests/test_imagefilter.py b/Tests/test_imagefilter.py new file mode 100644 index 000000000..214f88024 --- /dev/null +++ b/Tests/test_imagefilter.py @@ -0,0 +1,31 @@ +from tester import * + +from PIL import Image +from PIL import ImageFilter + +def test_sanity(): + # see test_image_filter for more tests + + assert_no_exception(lambda: ImageFilter.MaxFilter) + assert_no_exception(lambda: ImageFilter.MedianFilter) + assert_no_exception(lambda: ImageFilter.MinFilter) + assert_no_exception(lambda: ImageFilter.ModeFilter) + assert_no_exception(lambda: ImageFilter.Kernel((3, 3), list(range(9)))) + assert_no_exception(lambda: ImageFilter.GaussianBlur) + assert_no_exception(lambda: ImageFilter.GaussianBlur(5)) + assert_no_exception(lambda: ImageFilter.UnsharpMask) + assert_no_exception(lambda: ImageFilter.UnsharpMask(10)) + + assert_no_exception(lambda: ImageFilter.BLUR) + assert_no_exception(lambda: ImageFilter.CONTOUR) + assert_no_exception(lambda: ImageFilter.DETAIL) + assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE) + assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE_MORE) + assert_no_exception(lambda: ImageFilter.EMBOSS) + assert_no_exception(lambda: ImageFilter.FIND_EDGES) + assert_no_exception(lambda: ImageFilter.SMOOTH) + assert_no_exception(lambda: ImageFilter.SMOOTH_MORE) + assert_no_exception(lambda: ImageFilter.SHARPEN) + + + diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py new file mode 100644 index 000000000..9ac2cdd89 --- /dev/null +++ b/Tests/test_imagefont.py @@ -0,0 +1,135 @@ +from tester import * + +from PIL import Image +from io import BytesIO +import os + +try: + from PIL import ImageFont + ImageFont.core.getfont # check if freetype is available +except ImportError: + skip() + +from PIL import ImageDraw + +font_path = "Tests/fonts/FreeMono.ttf" +font_size=20 + +def test_sanity(): + assert_match(ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") + +def test_font_with_name(): + assert_no_exception(lambda: ImageFont.truetype(font_path, font_size)) + assert_no_exception(lambda: _render(font_path)) + _clean() + +def _font_as_bytes(): + with open(font_path, 'rb') as f: + font_bytes = BytesIO(f.read()) + return font_bytes + +def test_font_with_filelike(): + assert_no_exception(lambda: ImageFont.truetype(_font_as_bytes(), font_size)) + assert_no_exception(lambda: _render(_font_as_bytes())) + # Usage note: making two fonts from the same buffer fails. + #shared_bytes = _font_as_bytes() + #assert_no_exception(lambda: _render(shared_bytes)) + #assert_exception(Exception, lambda: _render(shared_bytes)) + _clean() + +def test_font_with_open_file(): + with open(font_path, 'rb') as f: + assert_no_exception(lambda: _render(f)) + _clean() + +def test_font_old_parameters(): + assert_warning(DeprecationWarning, lambda: ImageFont.truetype(filename=font_path, size=font_size)) + +def _render(font): + txt = "Hello World!" + ttf = ImageFont.truetype(font, font_size) + w, h = ttf.getsize(txt) + img = Image.new("RGB", (256, 64), "white") + d = ImageDraw.Draw(img) + d.text((10, 10), txt, font=ttf, fill='black') + + img.save('font.png') + return img + +def _clean(): + os.unlink('font.png') + +def test_render_equal(): + img_path = _render(font_path) + with open(font_path, 'rb') as f: + font_filelike = BytesIO(f.read()) + img_filelike = _render(font_filelike) + + assert_image_equal(img_path, img_filelike) + _clean() + + +def test_render_multiline(): + im = Image.new(mode='RGB', size=(300,100)) + draw = ImageDraw.Draw(im) + ttf = ImageFont.truetype(font_path, font_size) + line_spacing = draw.textsize('A', font=ttf)[1] + 8 + lines = ['hey you', 'you are awesome', 'this looks awkward'] + y = 0 + for line in lines: + draw.text((0, y), line, font=ttf) + y += line_spacing + + + target = 'Tests/images/multiline_text.png' + target_img = Image.open(target) + + # some versions of freetype have different horizontal spacing. + # setting a tight epsilon, I'm showing the original test failure + # at epsilon = ~38. + assert_image_similar(im, target_img,.5) + + +def test_rotated_transposed_font(): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = Image.ROTATE_90 + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check (w,h) of box a is (h,w) of box b + assert_equal(box_size_a[0], box_size_b[1]) + assert_equal(box_size_a[1], box_size_b[0]) + + +def test_unrotated_transposed_font(): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = None + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check boxes a and b are same size + assert_equal(box_size_a, box_size_b) + + diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py new file mode 100644 index 000000000..67ff71960 --- /dev/null +++ b/Tests/test_imagegrab.py @@ -0,0 +1,13 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageGrab +except ImportError as v: + skip(v) + +def test_grab(): + im = ImageGrab.grab() + assert_image(im, im.mode, im.size) + + diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py new file mode 100644 index 000000000..eaeb711ba --- /dev/null +++ b/Tests/test_imagemath.py @@ -0,0 +1,62 @@ +from tester import * + +from PIL import Image +from PIL import ImageMath + +def pixel(im): + if hasattr(im, "im"): + return "%s %r" % (im.mode, im.getpixel((0, 0))) + else: + if isinstance(im, type(0)): + return int(im) # hack to deal with booleans + print(im) + +A = Image.new("L", (1, 1), 1) +B = Image.new("L", (1, 1), 2) +F = Image.new("F", (1, 1), 3) +I = Image.new("I", (1, 1), 4) + +images = {"A": A, "B": B, "F": F, "I": I} + +def test_sanity(): + assert_equal(ImageMath.eval("1"), 1) + assert_equal(ImageMath.eval("1+A", A=2), 3) + assert_equal(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") + assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") + assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + assert_equal(pixel(ImageMath.eval("int(float(A)+B)", images)), "I 3") + +def test_ops(): + + assert_equal(pixel(ImageMath.eval("-A", images)), "I -1") + assert_equal(pixel(ImageMath.eval("+B", images)), "L 2") + + assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") + assert_equal(pixel(ImageMath.eval("A-B", images)), "I -1") + assert_equal(pixel(ImageMath.eval("A*B", images)), "I 2") + assert_equal(pixel(ImageMath.eval("A/B", images)), "I 0") + assert_equal(pixel(ImageMath.eval("B**2", images)), "I 4") + assert_equal(pixel(ImageMath.eval("B**33", images)), "I 2147483647") + + assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + assert_equal(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") + assert_equal(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") + assert_equal(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") + assert_equal(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") + assert_equal(pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0") + +def test_logical(): + assert_equal(pixel(ImageMath.eval("not A", images)), 0) + assert_equal(pixel(ImageMath.eval("A and B", images)), "L 2") + assert_equal(pixel(ImageMath.eval("A or B", images)), "L 1") + +def test_convert(): + assert_equal(pixel(ImageMath.eval("convert(A+B, 'L')", images)), "L 3") + assert_equal(pixel(ImageMath.eval("convert(A+B, '1')", images)), "1 0") + assert_equal(pixel(ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") + +def test_compare(): + assert_equal(pixel(ImageMath.eval("min(A, B)", images)), "I 1") + assert_equal(pixel(ImageMath.eval("max(A, B)", images)), "I 2") + assert_equal(pixel(ImageMath.eval("A == 1", images)), "I 1") + assert_equal(pixel(ImageMath.eval("A == 2", images)), "I 0") diff --git a/Tests/test_imagemode.py b/Tests/test_imagemode.py new file mode 100644 index 000000000..54b04435f --- /dev/null +++ b/Tests/test_imagemode.py @@ -0,0 +1,23 @@ +from tester import * + +from PIL import Image +from PIL import ImageMode + +ImageMode.getmode("1") +ImageMode.getmode("L") +ImageMode.getmode("P") +ImageMode.getmode("RGB") +ImageMode.getmode("I") +ImageMode.getmode("F") + +m = ImageMode.getmode("1") +assert_equal(m.mode, "1") +assert_equal(m.bands, ("1",)) +assert_equal(m.basemode, "L") +assert_equal(m.basetype, "L") + +m = ImageMode.getmode("RGB") +assert_equal(m.mode, "RGB") +assert_equal(m.bands, ("R", "G", "B")) +assert_equal(m.basemode, "RGB") +assert_equal(m.basetype, "L") diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py new file mode 100644 index 000000000..8ed5ccefa --- /dev/null +++ b/Tests/test_imageops.py @@ -0,0 +1,81 @@ +from tester import * + +from PIL import Image +from PIL import ImageOps + +class Deformer: + def getmesh(self, im): + x, y = im.size + return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] + +deformer = Deformer() + +def test_sanity(): + + ImageOps.autocontrast(lena("L")) + ImageOps.autocontrast(lena("RGB")) + + ImageOps.autocontrast(lena("L"), cutoff=10) + ImageOps.autocontrast(lena("L"), ignore=[0, 255]) + + ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) + ImageOps.colorize(lena("L"), "black", "white") + + ImageOps.crop(lena("L"), 1) + ImageOps.crop(lena("RGB"), 1) + + ImageOps.deform(lena("L"), deformer) + ImageOps.deform(lena("RGB"), deformer) + + ImageOps.equalize(lena("L")) + ImageOps.equalize(lena("RGB")) + + ImageOps.expand(lena("L"), 1) + ImageOps.expand(lena("RGB"), 1) + ImageOps.expand(lena("L"), 2, "blue") + ImageOps.expand(lena("RGB"), 2, "blue") + + ImageOps.fit(lena("L"), (128, 128)) + ImageOps.fit(lena("RGB"), (128, 128)) + + ImageOps.flip(lena("L")) + ImageOps.flip(lena("RGB")) + + ImageOps.grayscale(lena("L")) + ImageOps.grayscale(lena("RGB")) + + ImageOps.invert(lena("L")) + ImageOps.invert(lena("RGB")) + + ImageOps.mirror(lena("L")) + ImageOps.mirror(lena("RGB")) + + ImageOps.posterize(lena("L"), 4) + ImageOps.posterize(lena("RGB"), 4) + + ImageOps.solarize(lena("L")) + ImageOps.solarize(lena("RGB")) + + success() + +def test_1pxfit(): + # Division by zero in equalize if image is 1 pixel high + newimg = ImageOps.fit(lena("RGB").resize((1,1)), (35,35)) + assert_equal(newimg.size,(35,35)) + + newimg = ImageOps.fit(lena("RGB").resize((1,100)), (35,35)) + assert_equal(newimg.size,(35,35)) + + newimg = ImageOps.fit(lena("RGB").resize((100,1)), (35,35)) + assert_equal(newimg.size,(35,35)) + +def test_pil163(): + # Division by zero in equalize if < 255 pixels in image (@PIL163) + + i = lena("RGB").resize((15, 16)) + + ImageOps.equalize(i.convert("L")) + ImageOps.equalize(i.convert("P")) + ImageOps.equalize(i.convert("RGB")) + + success() diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py new file mode 100644 index 000000000..99ec005c8 --- /dev/null +++ b/Tests/test_imageshow.py @@ -0,0 +1,6 @@ +from tester import * + +from PIL import Image +from PIL import ImageShow + +success() diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py new file mode 100644 index 000000000..02a461e22 --- /dev/null +++ b/Tests/test_imagestat.py @@ -0,0 +1,52 @@ +from tester import * + +from PIL import Image +from PIL import ImageStat + +def test_sanity(): + + im = lena() + + st = ImageStat.Stat(im) + st = ImageStat.Stat(im.histogram()) + st = ImageStat.Stat(im, Image.new("1", im.size, 1)) + + assert_no_exception(lambda: st.extrema) + assert_no_exception(lambda: st.sum) + assert_no_exception(lambda: st.mean) + assert_no_exception(lambda: st.median) + assert_no_exception(lambda: st.rms) + assert_no_exception(lambda: st.sum2) + assert_no_exception(lambda: st.var) + assert_no_exception(lambda: st.stddev) + assert_exception(AttributeError, lambda: st.spam) + + assert_exception(TypeError, lambda: ImageStat.Stat(1)) + +def test_lena(): + + im = lena() + + st = ImageStat.Stat(im) + + # verify a few values + assert_equal(st.extrema[0], (61, 255)) + assert_equal(st.median[0], 197) + assert_equal(st.sum[0], 2954416) + assert_equal(st.sum[1], 2027250) + assert_equal(st.sum[2], 1727331) + +def test_constant(): + + im = Image.new("L", (128, 128), 128) + + st = ImageStat.Stat(im) + + assert_equal(st.extrema[0], (128, 128)) + assert_equal(st.sum[0], 128**3) + assert_equal(st.sum2[0], 128**4) + assert_equal(st.mean[0], 128) + assert_equal(st.median[0], 128) + assert_equal(st.rms[0], 128) + assert_equal(st.var[0], 0) + assert_equal(st.stddev[0], 0) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py new file mode 100644 index 000000000..b30971e8f --- /dev/null +++ b/Tests/test_imagetk.py @@ -0,0 +1,9 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageTk +except (OSError, ImportError) as v: + skip(v) + +success() diff --git a/Tests/test_imagetransform.py b/Tests/test_imagetransform.py new file mode 100644 index 000000000..884e6bb1c --- /dev/null +++ b/Tests/test_imagetransform.py @@ -0,0 +1,18 @@ +from tester import * + +from PIL import Image +from PIL import ImageTransform + +im = Image.new("L", (100, 100)) + +seq = tuple(range(10)) + +def test_sanity(): + transform = ImageTransform.AffineTransform(seq[:6]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.ExtentTransform(seq[:4]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.QuadTransform(seq[:8]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) + assert_no_exception(lambda: im.transform((100, 100), transform)) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py new file mode 100644 index 000000000..268a75b6f --- /dev/null +++ b/Tests/test_imagewin.py @@ -0,0 +1,6 @@ +from tester import * + +from PIL import Image +from PIL import ImageWin + +success() diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py new file mode 100644 index 000000000..93aa694d8 --- /dev/null +++ b/Tests/test_lib_image.py @@ -0,0 +1,30 @@ +from tester import * + +from PIL import Image + +def test_setmode(): + + im = Image.new("L", (1, 1), 255) + im.im.setmode("1") + assert_equal(im.im.getpixel((0, 0)), 255) + im.im.setmode("L") + assert_equal(im.im.getpixel((0, 0)), 255) + + im = Image.new("1", (1, 1), 1) + im.im.setmode("L") + assert_equal(im.im.getpixel((0, 0)), 255) + im.im.setmode("1") + assert_equal(im.im.getpixel((0, 0)), 255) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.im.setmode("RGB") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + im.im.setmode("RGBA") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGBX") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGB") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + + assert_exception(ValueError, lambda: im.im.setmode("L")) + assert_exception(ValueError, lambda: im.im.setmode("RGBABCDE")) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py new file mode 100644 index 000000000..7675348b3 --- /dev/null +++ b/Tests/test_lib_pack.py @@ -0,0 +1,138 @@ +from tester import * + +from PIL import Image + +def pack(): + pass # not yet + +def test_pack(): + + def pack(mode, rawmode): + if len(mode) == 1: + im = Image.new(mode, (1, 1), 1) + else: + im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) + + if py3: + return list(im.tobytes("raw", rawmode)) + else: + return [ord(c) for c in im.tobytes("raw", rawmode)] + + order = 1 if Image._ENDIAN == '<' else -1 + + assert_equal(pack("1", "1"), [128]) + assert_equal(pack("1", "1;I"), [0]) + assert_equal(pack("1", "1;R"), [1]) + assert_equal(pack("1", "1;IR"), [0]) + + assert_equal(pack("L", "L"), [1]) + + assert_equal(pack("I", "I"), [1, 0, 0, 0][::order]) + + assert_equal(pack("F", "F"), [0, 0, 128, 63][::order]) + + assert_equal(pack("LA", "LA"), [1, 2]) + + assert_equal(pack("RGB", "RGB"), [1, 2, 3]) + assert_equal(pack("RGB", "RGB;L"), [1, 2, 3]) + assert_equal(pack("RGB", "BGR"), [3, 2, 1]) + assert_equal(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? + assert_equal(pack("RGB", "BGRX"), [3, 2, 1, 0]) + assert_equal(pack("RGB", "XRGB"), [0, 1, 2, 3]) + assert_equal(pack("RGB", "XBGR"), [0, 3, 2, 1]) + + assert_equal(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? + + assert_equal(pack("RGBA", "RGBA"), [1, 2, 3, 4]) + + assert_equal(pack("CMYK", "CMYK"), [1, 2, 3, 4]) + assert_equal(pack("YCbCr", "YCbCr"), [1, 2, 3]) + +def test_unpack(): + + def unpack(mode, rawmode, bytes_): + im = None + + if py3: + data = bytes(range(1,bytes_+1)) + else: + data = ''.join(chr(i) for i in range(1,bytes_+1)) + + im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) + + return im.getpixel((0, 0)) + + def unpack_1(mode, rawmode, value): + assert mode == "1" + im = None + + if py3: + im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) + else: + im = Image.frombytes(mode, (8, 1), chr(value), "raw", rawmode, 0, 1) + + return tuple(im.getdata()) + + X = 255 + + assert_equal(unpack_1("1", "1", 1), (0,0,0,0,0,0,0,X)) + assert_equal(unpack_1("1", "1;I", 1), (X,X,X,X,X,X,X,0)) + assert_equal(unpack_1("1", "1;R", 1), (X,0,0,0,0,0,0,0)) + assert_equal(unpack_1("1", "1;IR", 1), (0,X,X,X,X,X,X,X)) + + assert_equal(unpack_1("1", "1", 170), (X,0,X,0,X,0,X,0)) + assert_equal(unpack_1("1", "1;I", 170), (0,X,0,X,0,X,0,X)) + assert_equal(unpack_1("1", "1;R", 170), (0,X,0,X,0,X,0,X)) + assert_equal(unpack_1("1", "1;IR", 170), (X,0,X,0,X,0,X,0)) + + assert_equal(unpack("L", "L;2", 1), 0) + assert_equal(unpack("L", "L;4", 1), 0) + assert_equal(unpack("L", "L", 1), 1) + assert_equal(unpack("L", "L;I", 1), 254) + assert_equal(unpack("L", "L;R", 1), 128) + assert_equal(unpack("L", "L;16", 2), 2) # little endian + assert_equal(unpack("L", "L;16B", 2), 1) # big endian + + assert_equal(unpack("LA", "LA", 2), (1, 2)) + assert_equal(unpack("LA", "LA;L", 2), (1, 2)) + + assert_equal(unpack("RGB", "RGB", 3), (1, 2, 3)) + assert_equal(unpack("RGB", "RGB;L", 3), (1, 2, 3)) + assert_equal(unpack("RGB", "RGB;R", 3), (128, 64, 192)) + assert_equal(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? + assert_equal(unpack("RGB", "BGR", 3), (3, 2, 1)) + assert_equal(unpack("RGB", "RGB;15", 2), (8, 131, 0)) + assert_equal(unpack("RGB", "BGR;15", 2), (0, 131, 8)) + assert_equal(unpack("RGB", "RGB;16", 2), (8, 64, 0)) + assert_equal(unpack("RGB", "BGR;16", 2), (0, 64, 8)) + assert_equal(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) + + assert_equal(unpack("RGB", "RGBX", 4), (1, 2, 3)) + assert_equal(unpack("RGB", "BGRX", 4), (3, 2, 1)) + assert_equal(unpack("RGB", "XRGB", 4), (2, 3, 4)) + assert_equal(unpack("RGB", "XBGR", 4), (4, 3, 2)) + + assert_equal(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) + assert_equal(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) + assert_equal(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) + assert_equal(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) + assert_equal(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) + assert_equal(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) + assert_equal(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) + + assert_equal(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? + assert_equal(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) + assert_equal(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) + assert_equal(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) + assert_equal(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) + assert_equal(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) + assert_equal(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) + + assert_equal(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) + assert_equal(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) + + assert_exception(ValueError, lambda: unpack("L", "L", 0)) + assert_exception(ValueError, lambda: unpack("RGB", "RGB", 2)) + assert_exception(ValueError, lambda: unpack("CMYK", "CMYK", 2)) + +run() diff --git a/Tests/test_locale.py b/Tests/test_locale.py new file mode 100644 index 000000000..6b2b95201 --- /dev/null +++ b/Tests/test_locale.py @@ -0,0 +1,31 @@ +from tester import * +from PIL import Image + +import locale + +# ref https://github.com/python-pillow/Pillow/issues/272 +## on windows, in polish locale: + +## import locale +## print locale.setlocale(locale.LC_ALL, 'polish') +## import string +## print len(string.whitespace) +## print ord(string.whitespace[6]) + +## Polish_Poland.1250 +## 7 +## 160 + +# one of string.whitespace is not freely convertable into ascii. + +path = "Images/lena.jpg" + +def test_sanity(): + assert_no_exception(lambda: Image.open(path)) + try: + locale.setlocale(locale.LC_ALL, "polish") + except: + skip('polish locale not available') + import string + assert_no_exception(lambda: Image.open(path)) + diff --git a/test/helper.py b/test/helper.py deleted file mode 100644 index 567fc3945..000000000 --- a/test/helper.py +++ /dev/null @@ -1,310 +0,0 @@ -""" -Helper functions. -""" -from __future__ import print_function -import sys - -if sys.version_info[:2] <= (2, 6): - import unittest2 as unittest -else: - import unittest - - -class PillowTestCase(unittest.TestCase): - - def assert_image(self, im, mode, size, msg=None): - if mode is not None: - self.assertEqual( - im.mode, mode, - msg or "got mode %r, expected %r" % (im.mode, mode)) - - if size is not None: - self.assertEqual( - im.size, size, - msg or "got size %r, expected %r" % (im.size, size)) - - def assert_image_equal(self, a, b, msg=None): - self.assertEqual( - a.mode, b.mode, - msg or "got mode %r, expected %r" % (a.mode, b.mode)) - self.assertEqual( - a.size, b.size, - msg or "got size %r, expected %r" % (a.size, b.size)) - self.assertEqual( - a.tobytes(), b.tobytes(), - msg or "got different content") - - def assert_image_similar(self, a, b, epsilon, msg=None): - epsilon = float(epsilon) - self.assertEqual( - a.mode, b.mode, - msg or "got mode %r, expected %r" % (a.mode, b.mode)) - self.assertEqual( - a.size, b.size, - msg or "got size %r, expected %r" % (a.size, b.size)) - - diff = 0 - try: - ord(b'0') - for abyte, bbyte in zip(a.tobytes(), b.tobytes()): - diff += abs(ord(abyte)-ord(bbyte)) - except: - for abyte, bbyte in zip(a.tobytes(), b.tobytes()): - diff += abs(abyte-bbyte) - ave_diff = float(diff)/(a.size[0]*a.size[1]) - self.assertGreaterEqual( - epsilon, ave_diff, - msg or "average pixel value difference %.4f > epsilon %.4f" % ( - ave_diff, epsilon)) - - def assert_warning(self, warn_class, func): - import warnings - - result = None - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - - # Hopefully trigger a warning. - result = func() - - # Verify some things. - self.assertGreaterEqual(len(w), 1) - found = False - for v in w: - if issubclass(v.category, warn_class): - found = True - break - self.assertTrue(found) - return result - -# # require that deprecation warnings are triggered -# import warnings -# warnings.simplefilter('default') -# # temporarily turn off resource warnings that warn about unclosed -# # files in the test scripts. -# try: -# warnings.filterwarnings("ignore", category=ResourceWarning) -# except NameError: -# # we expect a NameError on py2.x, since it doesn't have ResourceWarnings. -# pass - -import sys -py3 = (sys.version_info >= (3, 0)) - -# # some test helpers -# -# _target = None -# _tempfiles = [] -# _logfile = None -# -# -# def success(): -# import sys -# success.count += 1 -# if _logfile: -# print(sys.argv[0], success.count, failure.count, file=_logfile) -# return True -# -# -# def failure(msg=None, frame=None): -# import sys -# import linecache -# failure.count += 1 -# if _target: -# if frame is None: -# frame = sys._getframe() -# while frame.f_globals.get("__name__") != _target.__name__: -# frame = frame.f_back -# location = (frame.f_code.co_filename, frame.f_lineno) -# prefix = "%s:%d: " % location -# line = linecache.getline(*location) -# print(prefix + line.strip() + " failed:") -# if msg: -# print("- " + msg) -# if _logfile: -# print(sys.argv[0], success.count, failure.count, file=_logfile) -# return False -# -# success.count = failure.count = 0 -# -# -# # predicates -# -# def assert_almost_equal(a, b, msg=None, eps=1e-6): -# if abs(a-b) < eps: -# success() -# else: -# failure(msg or "got %r, expected %r" % (a, b)) -# -# -# def assert_deep_equal(a, b, msg=None): -# try: -# if len(a) == len(b): -# if all([x == y for x, y in zip(a, b)]): -# success() -# else: -# failure(msg or "got %s, expected %s" % (a, b)) -# else: -# failure(msg or "got length %s, expected %s" % (len(a), len(b))) -# except: -# assert_equal(a, b, msg) -# -# -# def assert_match(v, pattern, msg=None): -# import re -# if re.match(pattern, v): -# success() -# else: -# failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) - - -# helpers - -def fromstring(data): - from io import BytesIO - from PIL import Image - return Image.open(BytesIO(data)) - - -def tostring(im, format, **options): - from io import BytesIO - out = BytesIO() - im.save(out, format, **options) - return out.getvalue() - - -def lena(mode="RGB", cache={}): - from PIL import Image - im = None - # im = cache.get(mode) - if im is None: - if mode == "RGB": - im = Image.open("Images/lena.ppm") - elif mode == "F": - im = lena("L").convert(mode) - elif mode[:4] == "I;16": - im = lena("I").convert(mode) - else: - im = lena("RGB").convert(mode) - # cache[mode] = im - return im - - -# def assert_image_completely_equal(a, b, msg=None): -# if a != b: -# failure(msg or "images different") -# else: -# success() -# -# -# def tempfile(template, *extra): -# import os -# import os.path -# import sys -# import tempfile -# files = [] -# root = os.path.join(tempfile.gettempdir(), 'pillow-tests') -# try: -# os.mkdir(root) -# except OSError: -# pass -# for temp in (template,) + extra: -# assert temp[:5] in ("temp.", "temp_") -# name = os.path.basename(sys.argv[0]) -# name = temp[:4] + os.path.splitext(name)[0][4:] -# name = name + "_%d" % len(_tempfiles) + temp[4:] -# name = os.path.join(root, name) -# files.append(name) -# _tempfiles.extend(files) -# return files[0] -# -# -# # test runner -# -# def run(): -# global _target, _tests, run -# import sys -# import traceback -# _target = sys.modules["__main__"] -# run = None # no need to run twice -# tests = [] -# for name, value in list(vars(_target).items()): -# if name[:5] == "test_" and type(value) is type(success): -# tests.append((value.__code__.co_firstlineno, name, value)) -# tests.sort() # sort by line -# for lineno, name, func in tests: -# try: -# _tests = [] -# func() -# for func, args in _tests: -# func(*args) -# except: -# t, v, tb = sys.exc_info() -# tb = tb.tb_next -# if tb: -# failure(frame=tb.tb_frame) -# traceback.print_exception(t, v, tb) -# else: -# print("%s:%d: cannot call test function: %s" % ( -# sys.argv[0], lineno, v)) -# failure.count += 1 -# -# -# def yield_test(function, *args): -# # collect delayed/generated tests -# _tests.append((function, args)) -# -# -# def skip(msg=None): -# import os -# print("skip") -# os._exit(0) # don't run exit handlers -# -# -# def ignore(pattern): -# """Tells the driver to ignore messages matching the pattern, for the -# duration of the current test.""" -# print('ignore: %s' % pattern) -# -# -# def _setup(): -# global _logfile -# -# import sys -# if "--coverage" in sys.argv: -# # Temporary: ignore PendingDeprecationWarning from Coverage (Py3.4) -# with warnings.catch_warnings(): -# warnings.simplefilter("ignore") -# import coverage -# cov = coverage.coverage(auto_data=True, include="PIL/*") -# cov.start() -# -# def report(): -# if run: -# run() -# if success.count and not failure.count: -# print("ok") -# # only clean out tempfiles if test passed -# import os -# import os.path -# import tempfile -# for file in _tempfiles: -# try: -# os.remove(file) -# except OSError: -# pass # report? -# temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') -# try: -# os.rmdir(temp_root) -# except OSError: -# pass -# -# import atexit -# atexit.register(report) -# -# if "--log" in sys.argv: -# _logfile = open("test.log", "a") -# -# -# _setup() diff --git a/test/test_000_sanity.py b/test/test_000_sanity.py deleted file mode 100644 index 22e582ec3..000000000 --- a/test/test_000_sanity.py +++ /dev/null @@ -1,32 +0,0 @@ -from helper import unittest, PillowTestCase - -import PIL -import PIL.Image - - -class TestSanity(PillowTestCase): - - def test_sanity(self): - - # Make sure we have the binary extension - im = PIL.Image.core.new("L", (100, 100)) - - self.assertEqual(PIL.Image.VERSION[:3], '1.1') - - # Create an image and do stuff with it. - im = PIL.Image.new("1", (100, 100)) - self.assertEqual((im.mode, im.size), ('1', (100, 100))) - self.assertEqual(len(im.tobytes()), 1300) - - # Create images in all remaining major modes. - im = PIL.Image.new("L", (100, 100)) - im = PIL.Image.new("P", (100, 100)) - im = PIL.Image.new("RGB", (100, 100)) - im = PIL.Image.new("I", (100, 100)) - im = PIL.Image.new("F", (100, 100)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_bmp_reference.py b/test/test_bmp_reference.py deleted file mode 100644 index ed012e7c8..000000000 --- a/test/test_bmp_reference.py +++ /dev/null @@ -1,94 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image -import os - -base = os.path.join('Tests', 'images', 'bmp') - - -class TestImage(PillowTestCase): - - def get_files(self, d, ext='.bmp'): - return [os.path.join(base, d, f) for f - in os.listdir(os.path.join(base, d)) if ext in f] - - def test_bad(self): - """ These shouldn't crash/dos, but they shouldn't return anything - either """ - for f in self.get_files('b'): - try: - im = Image.open(f) - im.load() - except Exception: # as msg: - pass - # print ("Bad Image %s: %s" %(f,msg)) - - def test_questionable(self): - """ These shouldn't crash/dos, but its not well defined that these - are in spec """ - for f in self.get_files('q'): - try: - im = Image.open(f) - im.load() - except Exception: # as msg: - pass - # print ("Bad Image %s: %s" %(f,msg)) - - def test_good(self): - """ These should all work. There's a set of target files in the - html directory that we can compare against. """ - - # Target files, if they're not just replacing the extension - file_map = {'pal1wb.bmp': 'pal1.png', - 'pal4rle.bmp': 'pal4.png', - 'pal8-0.bmp': 'pal8.png', - 'pal8rle.bmp': 'pal8.png', - 'pal8topdown.bmp': 'pal8.png', - 'pal8nonsquare.bmp': 'pal8nonsquare-v.png', - 'pal8os2.bmp': 'pal8.png', - 'pal8os2sp.bmp': 'pal8.png', - 'pal8os2v2.bmp': 'pal8.png', - 'pal8os2v2-16.bmp': 'pal8.png', - 'pal8v4.bmp': 'pal8.png', - 'pal8v5.bmp': 'pal8.png', - 'rgb16-565pal.bmp': 'rgb16-565.png', - 'rgb24pal.bmp': 'rgb24.png', - 'rgb32.bmp': 'rgb24.png', - 'rgb32bf.bmp': 'rgb24.png' - } - - def get_compare(f): - (head, name) = os.path.split(f) - if name in file_map: - return os.path.join(base, 'html', file_map[name]) - (name, ext) = os.path.splitext(name) - return os.path.join(base, 'html', "%s.png" % name) - - for f in self.get_files('g'): - try: - im = Image.open(f) - im.load() - compare = Image.open(get_compare(f)) - compare.load() - if im.mode == 'P': - # assert image similar doesn't really work - # with paletized image, since the palette might - # be differently ordered for an equivalent image. - im = im.convert('RGBA') - compare = im.convert('RGBA') - self.assert_image_similar(im, compare, 5) - - except Exception as msg: - # there are three here that are unsupported: - unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), - os.path.join(base, 'g', 'pal8rle.bmp'), - os.path.join(base, 'g', 'pal4rle.bmp')) - if f not in unsupported: - self.assertTrue( - False, "Unsupported Image %s: %s" % (f, msg)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_file_fli.py b/test/test_file_fli.py deleted file mode 100644 index dd22a58f9..000000000 --- a/test/test_file_fli.py +++ /dev/null @@ -1,23 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - -# sample ppm stream -file = "Images/lena.fli" -data = open(file, "rb").read() - - -class TestImage(PillowTestCase): - - def test_sanity(self): - im = Image.open(file) - im.load() - self.assertEqual(im.mode, "P") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "FLI") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_file_icns.py b/test/test_file_icns.py deleted file mode 100644 index 9d838620b..000000000 --- a/test/test_file_icns.py +++ /dev/null @@ -1,74 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - -# sample icon file -file = "Images/pillow.icns" -data = open(file, "rb").read() - -enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') - - -class TestFileIcns(PillowTestCase): - - def test_sanity(self): - # Loading this icon by default should result in the largest size - # (512x512@2x) being loaded - im = Image.open(file) - im.load() - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (1024, 1024)) - self.assertEqual(im.format, "ICNS") - - def test_sizes(self): - # Check that we can load all of the sizes, and that the final pixel - # dimensions are as expected - im = Image.open(file) - for w, h, r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open(file) - im2.size = (w, h, r) - im2.load() - self.assertEqual(im2.mode, 'RGBA') - self.assertEqual(im2.size, (wr, hr)) - - def test_older_icon(self): - # This icon was made with Icon Composer rather than iconutil; it still - # uses PNG rather than JP2, however (since it was made on 10.9). - im = Image.open('Tests/images/pillow2.icns') - for w, h, r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open('Tests/images/pillow2.icns') - im2.size = (w, h, r) - im2.load() - self.assertEqual(im2.mode, 'RGBA') - self.assertEqual(im2.size, (wr, hr)) - - def test_jp2_icon(self): - # This icon was made by using Uli Kusterer's oldiconutil to replace - # the PNG images with JPEG 2000 ones. The advantage of doing this is - # that OS X 10.5 supports JPEG 2000 but not PNG; some commercial - # software therefore does just this. - - # (oldiconutil is here: https://github.com/uliwitness/oldiconutil) - - if not enable_jpeg2k: - return - - im = Image.open('Tests/images/pillow3.icns') - for w, h, r in im.info['sizes']: - wr = w * r - hr = h * r - im2 = Image.open('Tests/images/pillow3.icns') - im2.size = (w, h, r) - im2.load() - self.assertEqual(im2.mode, 'RGBA') - self.assertEqual(im2.size, (wr, hr)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_file_ico.py b/test/test_file_ico.py deleted file mode 100644 index dc289e1d2..000000000 --- a/test/test_file_ico.py +++ /dev/null @@ -1,23 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - -# sample ppm stream -file = "Images/lena.ico" -data = open(file, "rb").read() - - -class TestFileIco(PillowTestCase): - - def test_sanity(self): - im = Image.open(file) - im.load() - self.assertEqual(im.mode, "RGBA") - self.assertEqual(im.size, (16, 16)) - self.assertEqual(im.format, "ICO") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_file_psd.py b/test/test_file_psd.py deleted file mode 100644 index de3d6f33d..000000000 --- a/test/test_file_psd.py +++ /dev/null @@ -1,23 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - -# sample ppm stream -file = "Images/lena.psd" -data = open(file, "rb").read() - - -class TestImagePsd(PillowTestCase): - - def test_sanity(self): - im = Image.open(file) - im.load() - self.assertEqual(im.mode, "RGB") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "PSD") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_file_xpm.py b/test/test_file_xpm.py deleted file mode 100644 index ecbb4137a..000000000 --- a/test/test_file_xpm.py +++ /dev/null @@ -1,23 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - -# sample ppm stream -file = "Images/lena.xpm" -data = open(file, "rb").read() - - -class TestFileXpm(PillowTestCase): - - def test_sanity(self): - im = Image.open(file) - im.load() - self.assertEqual(im.mode, "P") - self.assertEqual(im.size, (128, 128)) - self.assertEqual(im.format, "XPM") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_font_bdf.py b/test/test_font_bdf.py deleted file mode 100644 index b141e6149..000000000 --- a/test/test_font_bdf.py +++ /dev/null @@ -1,22 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import FontFile, BdfFontFile - -filename = "Images/courB08.bdf" - - -class TestImage(PillowTestCase): - - def test_sanity(self): - - file = open(filename, "rb") - font = BdfFontFile.BdfFontFile(file) - - self.assertIsInstance(font, FontFile.FontFile) - self.assertEqual(len([_f for _f in font.glyph if _f]), 190) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_format_lab.py b/test/test_format_lab.py deleted file mode 100644 index 53468db5f..000000000 --- a/test/test_format_lab.py +++ /dev/null @@ -1,48 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - - -class TestFormatLab(PillowTestCase): - - def test_white(self): - i = Image.open('Tests/images/lab.tif') - - i.load() - - self.assertEqual(i.mode, 'LAB') - - self.assertEqual(i.getbands(), ('L', 'A', 'B')) - - k = i.getpixel((0, 0)) - self.assertEqual(k, (255, 128, 128)) - - L = i.getdata(0) - a = i.getdata(1) - b = i.getdata(2) - - self.assertEqual(list(L), [255]*100) - self.assertEqual(list(a), [128]*100) - self.assertEqual(list(b), [128]*100) - - def test_green(self): - # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS - # == RGB: 0, 152, 117 - i = Image.open('Tests/images/lab-green.tif') - - k = i.getpixel((0, 0)) - self.assertEqual(k, (128, 28, 128)) - - def test_red(self): - # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS - # == RGB: 255, 0, 124 - i = Image.open('Tests/images/lab-red.tif') - - k = i.getpixel((0, 0)) - self.assertEqual(k, (128, 228, 128)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_array.py b/test/test_image_array.py deleted file mode 100644 index a0f5f29e1..000000000 --- a/test/test_image_array.py +++ /dev/null @@ -1,46 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - -im = lena().resize((128, 100)) - - -class TestImageCrop(PillowTestCase): - - def test_toarray(self): - def test(mode): - ai = im.convert(mode).__array_interface__ - return ai["shape"], ai["typestr"], len(ai["data"]) - # self.assertEqual(test("1"), ((100, 128), '|b1', 1600)) - self.assertEqual(test("L"), ((100, 128), '|u1', 12800)) - - # FIXME: wrong? - self.assertEqual(test("I"), ((100, 128), Image._ENDIAN + 'i4', 51200)) - # FIXME: wrong? - self.assertEqual(test("F"), ((100, 128), Image._ENDIAN + 'f4', 51200)) - - self.assertEqual(test("RGB"), ((100, 128, 3), '|u1', 38400)) - self.assertEqual(test("RGBA"), ((100, 128, 4), '|u1', 51200)) - self.assertEqual(test("RGBX"), ((100, 128, 4), '|u1', 51200)) - - def test_fromarray(self): - def test(mode): - i = im.convert(mode) - a = i.__array_interface__ - a["strides"] = 1 # pretend it's non-contigous - i.__array_interface__ = a # patch in new version of attribute - out = Image.fromarray(i) - return out.mode, out.size, list(i.getdata()) == list(out.getdata()) - # self.assertEqual(test("1"), ("1", (128, 100), True)) - self.assertEqual(test("L"), ("L", (128, 100), True)) - self.assertEqual(test("I"), ("I", (128, 100), True)) - self.assertEqual(test("F"), ("F", (128, 100), True)) - self.assertEqual(test("RGB"), ("RGB", (128, 100), True)) - self.assertEqual(test("RGBA"), ("RGBA", (128, 100), True)) - self.assertEqual(test("RGBX"), ("RGBA", (128, 100), True)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_copy.py b/test/test_image_copy.py deleted file mode 100644 index a7882db94..000000000 --- a/test/test_image_copy.py +++ /dev/null @@ -1,20 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - - -class TestImageCopy(PillowTestCase): - - def test_copy(self): - def copy(mode): - im = lena(mode) - out = im.copy() - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size) - for mode in "1", "P", "L", "RGB", "I", "F": - copy(mode) - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_crop.py b/test/test_image_crop.py deleted file mode 100644 index da93fe7c8..000000000 --- a/test/test_image_crop.py +++ /dev/null @@ -1,59 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - - -class TestImageCrop(PillowTestCase): - - def test_crop(self): - def crop(mode): - out = lena(mode).crop((50, 50, 100, 100)) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, (50, 50)) - for mode in "1", "P", "L", "RGB", "I", "F": - crop(mode) - - def test_wide_crop(self): - - def crop(*bbox): - i = im.crop(bbox) - h = i.histogram() - while h and not h[-1]: - del h[-1] - return tuple(h) - - im = Image.new("L", (100, 100), 1) - - self.assertEqual(crop(0, 0, 100, 100), (0, 10000)) - self.assertEqual(crop(25, 25, 75, 75), (0, 2500)) - - # sides - self.assertEqual(crop(-25, 0, 25, 50), (1250, 1250)) - self.assertEqual(crop(0, -25, 50, 25), (1250, 1250)) - self.assertEqual(crop(75, 0, 125, 50), (1250, 1250)) - self.assertEqual(crop(0, 75, 50, 125), (1250, 1250)) - - self.assertEqual(crop(-25, 25, 125, 75), (2500, 5000)) - self.assertEqual(crop(25, -25, 75, 125), (2500, 5000)) - - # corners - self.assertEqual(crop(-25, -25, 25, 25), (1875, 625)) - self.assertEqual(crop(75, -25, 125, 25), (1875, 625)) - self.assertEqual(crop(75, 75, 125, 125), (1875, 625)) - self.assertEqual(crop(-25, 75, 25, 125), (1875, 625)) - - def test_negative_crop(self): - # Check negative crop size (@PIL171) - - im = Image.new("L", (512, 512)) - im = im.crop((400, 400, 200, 200)) - - self.assertEqual(im.size, (0, 0)) - self.assertEqual(len(im.getdata()), 0) - self.assertRaises(IndexError, lambda: im.getdata()[0]) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_filter.py b/test/test_image_filter.py deleted file mode 100644 index 4a85b0a2e..000000000 --- a/test/test_image_filter.py +++ /dev/null @@ -1,91 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image -from PIL import ImageFilter - - -class TestImageFilter(PillowTestCase): - - def test_sanity(self): - - def filter(filter): - im = lena("L") - out = im.filter(filter) - self.assertEqual(out.mode, im.mode) - self.assertEqual(out.size, im.size) - - filter(ImageFilter.BLUR) - filter(ImageFilter.CONTOUR) - filter(ImageFilter.DETAIL) - filter(ImageFilter.EDGE_ENHANCE) - filter(ImageFilter.EDGE_ENHANCE_MORE) - filter(ImageFilter.EMBOSS) - filter(ImageFilter.FIND_EDGES) - filter(ImageFilter.SMOOTH) - filter(ImageFilter.SMOOTH_MORE) - filter(ImageFilter.SHARPEN) - filter(ImageFilter.MaxFilter) - filter(ImageFilter.MedianFilter) - filter(ImageFilter.MinFilter) - filter(ImageFilter.ModeFilter) - filter(ImageFilter.Kernel((3, 3), list(range(9)))) - - self.assertRaises(TypeError, lambda: filter("hello")) - - def test_crash(self): - - # crashes on small images - im = Image.new("RGB", (1, 1)) - im.filter(ImageFilter.SMOOTH) - - im = Image.new("RGB", (2, 2)) - im.filter(ImageFilter.SMOOTH) - - im = Image.new("RGB", (3, 3)) - im.filter(ImageFilter.SMOOTH) - - def test_modefilter(self): - - def modefilter(mode): - im = Image.new(mode, (3, 3), None) - im.putdata(list(range(9))) - # image is: - # 0 1 2 - # 3 4 5 - # 6 7 8 - mod = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) - im.putdata([0, 0, 1, 2, 5, 1, 5, 2, 0]) # mode=0 - mod2 = im.filter(ImageFilter.ModeFilter).getpixel((1, 1)) - return mod, mod2 - - self.assertEqual(modefilter("1"), (4, 0)) - self.assertEqual(modefilter("L"), (4, 0)) - self.assertEqual(modefilter("P"), (4, 0)) - self.assertEqual(modefilter("RGB"), ((4, 0, 0), (0, 0, 0))) - - def test_rankfilter(self): - - def rankfilter(mode): - im = Image.new(mode, (3, 3), None) - im.putdata(list(range(9))) - # image is: - # 0 1 2 - # 3 4 5 - # 6 7 8 - min = im.filter(ImageFilter.MinFilter).getpixel((1, 1)) - med = im.filter(ImageFilter.MedianFilter).getpixel((1, 1)) - max = im.filter(ImageFilter.MaxFilter).getpixel((1, 1)) - return min, med, max - - self.assertEqual(rankfilter("1"), (0, 4, 8)) - self.assertEqual(rankfilter("L"), (0, 4, 8)) - self.assertRaises(ValueError, lambda: rankfilter("P")) - self.assertEqual(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0))) - self.assertEqual(rankfilter("I"), (0, 4, 8)) - self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_frombytes.py b/test/test_image_frombytes.py deleted file mode 100644 index aad8046a1..000000000 --- a/test/test_image_frombytes.py +++ /dev/null @@ -1,18 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - - -class TestImageFromBytes(PillowTestCase): - - def test_sanity(self): - im1 = lena() - im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes()) - - self.assert_image_equal(im1, im2) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_getbands.py b/test/test_image_getbands.py deleted file mode 100644 index e803abb02..000000000 --- a/test/test_image_getbands.py +++ /dev/null @@ -1,26 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - - -class TestImageGetBands(PillowTestCase): - - def test_getbands(self): - self.assertEqual(Image.new("1", (1, 1)).getbands(), ("1",)) - self.assertEqual(Image.new("L", (1, 1)).getbands(), ("L",)) - self.assertEqual(Image.new("I", (1, 1)).getbands(), ("I",)) - self.assertEqual(Image.new("F", (1, 1)).getbands(), ("F",)) - self.assertEqual(Image.new("P", (1, 1)).getbands(), ("P",)) - self.assertEqual(Image.new("RGB", (1, 1)).getbands(), ("R", "G", "B")) - self.assertEqual( - Image.new("RGBA", (1, 1)).getbands(), ("R", "G", "B", "A")) - self.assertEqual( - Image.new("CMYK", (1, 1)).getbands(), ("C", "M", "Y", "K")) - self.assertEqual( - Image.new("YCbCr", (1, 1)).getbands(), ("Y", "Cb", "Cr")) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_getbbox.py b/test/test_image_getbbox.py deleted file mode 100644 index 83a6a3dec..000000000 --- a/test/test_image_getbbox.py +++ /dev/null @@ -1,45 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - - -class TestImage(PillowTestCase): - - def test_sanity(self): - - bbox = lena().getbbox() - self.assertIsInstance(bbox, tuple) - - def test_bbox(self): - - # 8-bit mode - im = Image.new("L", (100, 100), 0) - self.assertEqual(im.getbbox(), None) - - im.paste(255, (10, 25, 90, 75)) - self.assertEqual(im.getbbox(), (10, 25, 90, 75)) - - im.paste(255, (25, 10, 75, 90)) - self.assertEqual(im.getbbox(), (10, 10, 90, 90)) - - im.paste(255, (-10, -10, 110, 110)) - self.assertEqual(im.getbbox(), (0, 0, 100, 100)) - - # 32-bit mode - im = Image.new("RGB", (100, 100), 0) - self.assertEqual(im.getbbox(), None) - - im.paste(255, (10, 25, 90, 75)) - self.assertEqual(im.getbbox(), (10, 25, 90, 75)) - - im.paste(255, (25, 10, 75, 90)) - self.assertEqual(im.getbbox(), (10, 10, 90, 90)) - - im.paste(255, (-10, -10, 110, 110)) - self.assertEqual(im.getbbox(), (0, 0, 100, 100)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_getcolors.py b/test/test_image_getcolors.py deleted file mode 100644 index cfc827b28..000000000 --- a/test/test_image_getcolors.py +++ /dev/null @@ -1,74 +0,0 @@ -from helper import unittest, PillowTestCase, lena - - -class TestImage(PillowTestCase): - - def test_getcolors(self): - - def getcolors(mode, limit=None): - im = lena(mode) - if limit: - colors = im.getcolors(limit) - else: - colors = im.getcolors() - if colors: - return len(colors) - return None - - self.assertEqual(getcolors("1"), 2) - self.assertEqual(getcolors("L"), 193) - self.assertEqual(getcolors("I"), 193) - self.assertEqual(getcolors("F"), 193) - self.assertEqual(getcolors("P"), 54) # fixed palette - self.assertEqual(getcolors("RGB"), None) - self.assertEqual(getcolors("RGBA"), None) - self.assertEqual(getcolors("CMYK"), None) - self.assertEqual(getcolors("YCbCr"), None) - - self.assertEqual(getcolors("L", 128), None) - self.assertEqual(getcolors("L", 1024), 193) - - self.assertEqual(getcolors("RGB", 8192), None) - self.assertEqual(getcolors("RGB", 16384), 14836) - self.assertEqual(getcolors("RGB", 100000), 14836) - - self.assertEqual(getcolors("RGBA", 16384), 14836) - self.assertEqual(getcolors("CMYK", 16384), 14836) - self.assertEqual(getcolors("YCbCr", 16384), 11995) - - # -------------------------------------------------------------------- - - def test_pack(self): - # Pack problems for small tables (@PIL209) - - im = lena().quantize(3).convert("RGB") - - expected = [ - (3236, (227, 183, 147)), - (6297, (143, 84, 81)), - (6851, (208, 143, 112))] - - A = im.getcolors(maxcolors=2) - self.assertEqual(A, None) - - A = im.getcolors(maxcolors=3) - A.sort() - self.assertEqual(A, expected) - - A = im.getcolors(maxcolors=4) - A.sort() - self.assertEqual(A, expected) - - A = im.getcolors(maxcolors=8) - A.sort() - self.assertEqual(A, expected) - - A = im.getcolors(maxcolors=16) - A.sort() - self.assertEqual(A, expected) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_getextrema.py b/test/test_image_getextrema.py deleted file mode 100644 index af7f7698a..000000000 --- a/test/test_image_getextrema.py +++ /dev/null @@ -1,27 +0,0 @@ -from helper import unittest, PillowTestCase, lena - - -class TestImageGetExtrema(PillowTestCase): - - def test_extrema(self): - - def extrema(mode): - return lena(mode).getextrema() - - self.assertEqual(extrema("1"), (0, 255)) - self.assertEqual(extrema("L"), (40, 235)) - self.assertEqual(extrema("I"), (40, 235)) - self.assertEqual(extrema("F"), (40.0, 235.0)) - self.assertEqual(extrema("P"), (11, 218)) # fixed palette - self.assertEqual( - extrema("RGB"), ((61, 255), (26, 234), (44, 223))) - self.assertEqual( - extrema("RGBA"), ((61, 255), (26, 234), (44, 223), (255, 255))) - self.assertEqual( - extrema("CMYK"), ((0, 194), (21, 229), (32, 211), (0, 0))) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_getim.py b/test/test_image_getim.py deleted file mode 100644 index d498d3923..000000000 --- a/test/test_image_getim.py +++ /dev/null @@ -1,19 +0,0 @@ -from helper import unittest, PillowTestCase, lena, py3 - - -class TestImageGetIm(PillowTestCase): - - def test_sanity(self): - im = lena() - type_repr = repr(type(im.getim())) - - if py3: - self.assertIn("PyCapsule", type_repr) - - self.assertIsInstance(im.im.id, int) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_getpalette.py b/test/test_image_getpalette.py deleted file mode 100644 index 0c399c432..000000000 --- a/test/test_image_getpalette.py +++ /dev/null @@ -1,26 +0,0 @@ -from helper import unittest, PillowTestCase, lena - - -class TestImageGetPalette(PillowTestCase): - - def test_palette(self): - def palette(mode): - p = lena(mode).getpalette() - if p: - return p[:10] - return None - self.assertEqual(palette("1"), None) - self.assertEqual(palette("L"), None) - self.assertEqual(palette("I"), None) - self.assertEqual(palette("F"), None) - self.assertEqual(palette("P"), [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - self.assertEqual(palette("RGB"), None) - self.assertEqual(palette("RGBA"), None) - self.assertEqual(palette("CMYK"), None) - self.assertEqual(palette("YCbCr"), None) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_histogram.py b/test/test_image_histogram.py deleted file mode 100644 index 70f78a1fb..000000000 --- a/test/test_image_histogram.py +++ /dev/null @@ -1,26 +0,0 @@ -from helper import unittest, PillowTestCase, lena - - -class TestImageHistogram(PillowTestCase): - - def test_histogram(self): - - def histogram(mode): - h = lena(mode).histogram() - return len(h), min(h), max(h) - - self.assertEqual(histogram("1"), (256, 0, 8872)) - self.assertEqual(histogram("L"), (256, 0, 199)) - self.assertEqual(histogram("I"), (256, 0, 199)) - self.assertEqual(histogram("F"), (256, 0, 199)) - self.assertEqual(histogram("P"), (256, 0, 2912)) - self.assertEqual(histogram("RGB"), (768, 0, 285)) - self.assertEqual(histogram("RGBA"), (1024, 0, 16384)) - self.assertEqual(histogram("CMYK"), (1024, 0, 16384)) - self.assertEqual(histogram("YCbCr"), (768, 0, 741)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_load.py b/test/test_image_load.py deleted file mode 100644 index 2001c233a..000000000 --- a/test/test_image_load.py +++ /dev/null @@ -1,35 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - -import os - - -class TestImageLoad(PillowTestCase): - - def test_sanity(self): - - im = lena() - - pix = im.load() - - self.assertEqual(pix[0, 0], (223, 162, 133)) - - def test_close(self): - im = Image.open("Images/lena.gif") - im.close() - self.assertRaises(ValueError, lambda: im.load()) - self.assertRaises(ValueError, lambda: im.getpixel((0, 0))) - - def test_contextmanager(self): - fn = None - with Image.open("Images/lena.gif") as im: - fn = im.fp.fileno() - os.fstat(fn) - - self.assertRaises(OSError, lambda: os.fstat(fn)) - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_mode.py b/test/test_image_mode.py deleted file mode 100644 index d229a27a2..000000000 --- a/test/test_image_mode.py +++ /dev/null @@ -1,36 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - - -class TestImage(PillowTestCase): - - def test_sanity(self): - - im = lena() - im.mode - - def test_properties(self): - def check(mode, *result): - signature = ( - Image.getmodebase(mode), Image.getmodetype(mode), - Image.getmodebands(mode), Image.getmodebandnames(mode), - ) - self.assertEqual(signature, result) - check("1", "L", "L", 1, ("1",)) - check("L", "L", "L", 1, ("L",)) - check("P", "RGB", "L", 1, ("P",)) - check("I", "L", "I", 1, ("I",)) - check("F", "L", "F", 1, ("F",)) - check("RGB", "RGB", "L", 3, ("R", "G", "B")) - check("RGBA", "RGB", "L", 4, ("R", "G", "B", "A")) - check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) - check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) - check("CMYK", "RGB", "L", 4, ("C", "M", "Y", "K")) - check("YCbCr", "RGB", "L", 3, ("Y", "Cb", "Cr")) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_offset.py b/test/test_image_offset.py deleted file mode 100644 index bb9b5a38c..000000000 --- a/test/test_image_offset.py +++ /dev/null @@ -1,25 +0,0 @@ -from helper import unittest, PillowTestCase, lena - - -class TestImage(PillowTestCase): - - def test_offset(self): - - im1 = lena() - - im2 = self.assert_warning(DeprecationWarning, lambda: im1.offset(10)) - self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((10, 10))) - - im2 = self.assert_warning( - DeprecationWarning, lambda: im1.offset(10, 20)) - self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((10, 20))) - - im2 = self.assert_warning( - DeprecationWarning, lambda: im1.offset(20, 20)) - self.assertEqual(im1.getpixel((0, 0)), im2.getpixel((20, 20))) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_putalpha.py b/test/test_image_putalpha.py deleted file mode 100644 index 85c7ac262..000000000 --- a/test/test_image_putalpha.py +++ /dev/null @@ -1,52 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - - -class TestImagePutAlpha(PillowTestCase): - - def test_interface(self): - - im = Image.new("RGBA", (1, 1), (1, 2, 3, 0)) - self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 0)) - - im = Image.new("RGBA", (1, 1), (1, 2, 3)) - self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 255)) - - im.putalpha(Image.new("L", im.size, 4)) - self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) - - im.putalpha(5) - self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 5)) - - def test_promote(self): - - im = Image.new("L", (1, 1), 1) - self.assertEqual(im.getpixel((0, 0)), 1) - - im.putalpha(2) - self.assertEqual(im.mode, 'LA') - self.assertEqual(im.getpixel((0, 0)), (1, 2)) - - im = Image.new("RGB", (1, 1), (1, 2, 3)) - self.assertEqual(im.getpixel((0, 0)), (1, 2, 3)) - - im.putalpha(4) - self.assertEqual(im.mode, 'RGBA') - self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) - - def test_readonly(self): - - im = Image.new("RGB", (1, 1), (1, 2, 3)) - im.readonly = 1 - - im.putalpha(4) - self.assertFalse(im.readonly) - self.assertEqual(im.mode, 'RGBA') - self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_putdata.py b/test/test_image_putdata.py deleted file mode 100644 index c7c3115aa..000000000 --- a/test/test_image_putdata.py +++ /dev/null @@ -1,48 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -import sys - -from PIL import Image - - -class TestImagePutData(PillowTestCase): - - def test_sanity(self): - - im1 = lena() - - data = list(im1.getdata()) - - im2 = Image.new(im1.mode, im1.size, 0) - im2.putdata(data) - - self.assert_image_equal(im1, im2) - - # readonly - im2 = Image.new(im1.mode, im2.size, 0) - im2.readonly = 1 - im2.putdata(data) - - self.assertFalse(im2.readonly) - self.assert_image_equal(im1, im2) - - def test_long_integers(self): - # see bug-200802-systemerror - def put(value): - im = Image.new("RGBA", (1, 1)) - im.putdata([value]) - return im.getpixel((0, 0)) - self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) - self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) - self.assertEqual(put(-1), (255, 255, 255, 255)) - self.assertEqual(put(-1), (255, 255, 255, 255)) - if sys.maxsize > 2**32: - self.assertEqual(put(sys.maxsize), (255, 255, 255, 255)) - else: - self.assertEqual(put(sys.maxsize), (255, 255, 255, 127)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_putpalette.py b/test/test_image_putpalette.py deleted file mode 100644 index b77dcbf00..000000000 --- a/test/test_image_putpalette.py +++ /dev/null @@ -1,36 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import ImagePalette - - -class TestImage(PillowTestCase): - - def test_putpalette(self): - def palette(mode): - im = lena(mode).copy() - im.putpalette(list(range(256))*3) - p = im.getpalette() - if p: - return im.mode, p[:10] - return im.mode - self.assertRaises(ValueError, lambda: palette("1")) - self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) - self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) - self.assertRaises(ValueError, lambda: palette("I")) - self.assertRaises(ValueError, lambda: palette("F")) - self.assertRaises(ValueError, lambda: palette("RGB")) - self.assertRaises(ValueError, lambda: palette("RGBA")) - self.assertRaises(ValueError, lambda: palette("YCbCr")) - - def test_imagepalette(self): - im = lena("P") - im.putpalette(ImagePalette.negative()) - im.putpalette(ImagePalette.random()) - im.putpalette(ImagePalette.sepia()) - im.putpalette(ImagePalette.wedge()) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_quantize.py b/test/test_image_quantize.py deleted file mode 100644 index 35f876717..000000000 --- a/test/test_image_quantize.py +++ /dev/null @@ -1,35 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - - -class TestImage(PillowTestCase): - - def test_sanity(self): - im = lena() - - im = im.quantize() - self.assert_image(im, "P", im.size) - - im = lena() - im = im.quantize(palette=lena("P")) - self.assert_image(im, "P", im.size) - - def test_octree_quantize(self): - im = lena() - - im = im.quantize(100, Image.FASTOCTREE) - self.assert_image(im, "P", im.size) - - assert len(im.getcolors()) == 100 - - def test_rgba_quantize(self): - im = lena('RGBA') - im.quantize() - self.assertRaises(Exception, lambda: im.quantize(method=0)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_resize.py b/test/test_image_resize.py deleted file mode 100644 index 6c9932e45..000000000 --- a/test/test_image_resize.py +++ /dev/null @@ -1,19 +0,0 @@ -from helper import unittest, PillowTestCase, lena - - -class TestImageResize(PillowTestCase): - - def test_resize(self): - def resize(mode, size): - out = lena(mode).resize(size) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, size) - for mode in "1", "P", "L", "RGB", "I", "F": - resize(mode, (100, 100)) - resize(mode, (200, 200)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_rotate.py b/test/test_image_rotate.py deleted file mode 100644 index 531fdd63f..000000000 --- a/test/test_image_rotate.py +++ /dev/null @@ -1,22 +0,0 @@ -from helper import unittest, PillowTestCase, lena - - -class TestImageRotate(PillowTestCase): - - def test_rotate(self): - def rotate(mode): - im = lena(mode) - out = im.rotate(45) - self.assertEqual(out.mode, mode) - self.assertEqual(out.size, im.size) # default rotate clips output - out = im.rotate(45, expand=1) - self.assertEqual(out.mode, mode) - self.assertNotEqual(out.size, im.size) - for mode in "1", "P", "L", "RGB", "I", "F": - rotate(mode) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_thumbnail.py b/test/test_image_thumbnail.py deleted file mode 100644 index ee49be43e..000000000 --- a/test/test_image_thumbnail.py +++ /dev/null @@ -1,43 +0,0 @@ -from helper import unittest, PillowTestCase, lena - - -class TestImageThumbnail(PillowTestCase): - - def test_sanity(self): - - im = lena() - im.thumbnail((100, 100)) - - self.assert_image(im, im.mode, (100, 100)) - - def test_aspect(self): - - im = lena() - im.thumbnail((100, 100)) - self.assert_image(im, im.mode, (100, 100)) - - im = lena().resize((128, 256)) - im.thumbnail((100, 100)) - self.assert_image(im, im.mode, (50, 100)) - - im = lena().resize((128, 256)) - im.thumbnail((50, 100)) - self.assert_image(im, im.mode, (50, 100)) - - im = lena().resize((256, 128)) - im.thumbnail((100, 100)) - self.assert_image(im, im.mode, (100, 50)) - - im = lena().resize((256, 128)) - im.thumbnail((100, 50)) - self.assert_image(im, im.mode, (100, 50)) - - im = lena().resize((128, 128)) - im.thumbnail((100, 100)) - self.assert_image(im, im.mode, (100, 100)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_tobitmap.py b/test/test_image_tobitmap.py deleted file mode 100644 index 4e2a16df0..000000000 --- a/test/test_image_tobitmap.py +++ /dev/null @@ -1,22 +0,0 @@ -from helper import unittest, PillowTestCase, lena, fromstring - - -class TestImage(PillowTestCase): - - def test_sanity(self): - - self.assertRaises(ValueError, lambda: lena().tobitmap()) - lena().convert("1").tobitmap() - - im1 = lena().convert("1") - - bitmap = im1.tobitmap() - - self.assertIsInstance(bitmap, bytes) - self.assert_image_equal(im1, fromstring(bitmap)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_tobytes.py b/test/test_image_tobytes.py deleted file mode 100644 index 3be9128c1..000000000 --- a/test/test_image_tobytes.py +++ /dev/null @@ -1,13 +0,0 @@ -from helper import unittest, lena - - -class TestImageToBytes(unittest.TestCase): - - def test_sanity(self): - data = lena().tobytes() - self.assertTrue(isinstance(data, bytes)) - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_image_transform.py b/test/test_image_transform.py deleted file mode 100644 index 6ab186c12..000000000 --- a/test/test_image_transform.py +++ /dev/null @@ -1,125 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - - -class TestImageTransform(PillowTestCase): - - def test_extent(self): - im = lena('RGB') - (w, h) = im.size - transformed = im.transform(im.size, Image.EXTENT, - (0, 0, - w//2, h//2), # ul -> lr - Image.BILINEAR) - - scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h)) - - # undone -- precision? - self.assert_image_similar(transformed, scaled, 10) - - def test_quad(self): - # one simple quad transform, equivalent to scale & crop upper left quad - im = lena('RGB') - (w, h) = im.size - transformed = im.transform(im.size, Image.QUAD, - (0, 0, 0, h//2, - # ul -> ccw around quad: - w//2, h//2, w//2, 0), - Image.BILINEAR) - - scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h)) - - self.assert_image_equal(transformed, scaled) - - def test_mesh(self): - # this should be a checkerboard of halfsized lenas in ul, lr - im = lena('RGBA') - (w, h) = im.size - transformed = im.transform(im.size, Image.MESH, - [((0, 0, w//2, h//2), # box - (0, 0, 0, h, - w, h, w, 0)), # ul -> ccw around quad - ((w//2, h//2, w, h), # box - (0, 0, 0, h, - w, h, w, 0))], # ul -> ccw around quad - Image.BILINEAR) - - # transformed.save('transformed.png') - - scaled = im.resize((w//2, h//2), Image.BILINEAR) - - checker = Image.new('RGBA', im.size) - checker.paste(scaled, (0, 0)) - checker.paste(scaled, (w//2, h//2)) - - self.assert_image_equal(transformed, checker) - - # now, check to see that the extra area is (0, 0, 0, 0) - blank = Image.new('RGBA', (w//2, h//2), (0, 0, 0, 0)) - - self.assert_image_equal(blank, transformed.crop((w//2, 0, w, h//2))) - self.assert_image_equal(blank, transformed.crop((0, h//2, w//2, h))) - - def _test_alpha_premult(self, op): - # create image with half white, half black, - # with the black half transparent. - # do op, - # there should be no darkness in the white section. - im = Image.new('RGBA', (10, 10), (0, 0, 0, 0)) - im2 = Image.new('RGBA', (5, 10), (255, 255, 255, 255)) - im.paste(im2, (0, 0)) - - im = op(im, (40, 10)) - im_background = Image.new('RGB', (40, 10), (255, 255, 255)) - im_background.paste(im, (0, 0), im) - - hist = im_background.histogram() - self.assertEqual(40*10, hist[-1]) - - def test_alpha_premult_resize(self): - - def op(im, sz): - return im.resize(sz, Image.LINEAR) - - self._test_alpha_premult(op) - - def test_alpha_premult_transform(self): - - def op(im, sz): - (w, h) = im.size - return im.transform(sz, Image.EXTENT, - (0, 0, - w, h), - Image.BILINEAR) - - self._test_alpha_premult(op) - - def test_blank_fill(self): - # attempting to hit - # https://github.com/python-pillow/Pillow/issues/254 reported - # - # issue is that transforms with transparent overflow area - # contained junk from previous images, especially on systems with - # constrained memory. So, attempt to fill up memory with a - # pattern, free it, and then run the mesh test again. Using a 1Mp - # image with 4 bands, for 4 megs of data allocated, x 64. OMM (64 - # bit 12.04 VM with 512 megs available, this fails with Pillow < - # a0eaf06cc5f62a6fb6de556989ac1014ff3348ea - # - # Running by default, but I'd totally understand not doing it in - # the future - - foo = [Image.new('RGBA', (1024, 1024), (a, a, a, a)) - for a in range(1, 65)] - - # Yeah. Watch some JIT optimize this out. - foo = None - - self.test_mesh() - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagechops.py b/test/test_imagechops.py deleted file mode 100644 index ec162d52f..000000000 --- a/test/test_imagechops.py +++ /dev/null @@ -1,74 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image -from PIL import ImageChops - - -class TestImage(PillowTestCase): - - def test_sanity(self): - - im = lena("L") - - ImageChops.constant(im, 128) - ImageChops.duplicate(im) - ImageChops.invert(im) - ImageChops.lighter(im, im) - ImageChops.darker(im, im) - ImageChops.difference(im, im) - ImageChops.multiply(im, im) - ImageChops.screen(im, im) - - ImageChops.add(im, im) - ImageChops.add(im, im, 2.0) - ImageChops.add(im, im, 2.0, 128) - ImageChops.subtract(im, im) - ImageChops.subtract(im, im, 2.0) - ImageChops.subtract(im, im, 2.0, 128) - - ImageChops.add_modulo(im, im) - ImageChops.subtract_modulo(im, im) - - ImageChops.blend(im, im, 0.5) - ImageChops.composite(im, im, im) - - ImageChops.offset(im, 10) - ImageChops.offset(im, 10, 20) - - def test_logical(self): - - def table(op, a, b): - out = [] - for x in (a, b): - imx = Image.new("1", (1, 1), x) - for y in (a, b): - imy = Image.new("1", (1, 1), y) - out.append(op(imx, imy).getpixel((0, 0))) - return tuple(out) - - self.assertEqual( - table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) - self.assertEqual( - table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) - self.assertEqual( - table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) - - self.assertEqual( - table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) - self.assertEqual( - table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) - self.assertEqual( - table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) - - self.assertEqual( - table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) - self.assertEqual( - table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) - self.assertEqual( - table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagecms.py b/test/test_imagecms.py deleted file mode 100644 index 1a31636e8..000000000 --- a/test/test_imagecms.py +++ /dev/null @@ -1,214 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - -try: - from PIL import ImageCms - ImageCms.core.profile_open -except ImportError as v: - # Skipped via setUp() - pass - - -SRGB = "Tests/icc/sRGB.icm" - - -class TestImage(PillowTestCase): - - def setUp(self): - try: - from PIL import ImageCms - except ImportError as v: - self.skipTest(v) - - def test_sanity(self): - - # basic smoke test. - # this mostly follows the cms_test outline. - - v = ImageCms.versions() # should return four strings - self.assertEqual(v[0], '1.0.0 pil') - self.assertEqual(list(map(type, v)), [str, str, str, str]) - - # internal version number - self.assertRegexpMatches(ImageCms.core.littlecms_version, "\d+\.\d+$") - - i = ImageCms.profileToProfile(lena(), SRGB, SRGB) - self.assert_image(i, "RGB", (128, 128)) - - i = lena() - ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True) - self.assert_image(i, "RGB", (128, 128)) - - t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") - i = ImageCms.applyTransform(lena(), t) - self.assert_image(i, "RGB", (128, 128)) - - i = lena() - t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") - ImageCms.applyTransform(lena(), t, inPlace=True) - self.assert_image(i, "RGB", (128, 128)) - - p = ImageCms.createProfile("sRGB") - o = ImageCms.getOpenProfile(SRGB) - t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") - i = ImageCms.applyTransform(lena(), t) - self.assert_image(i, "RGB", (128, 128)) - - t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") - self.assertEqual(t.inputMode, "RGB") - self.assertEqual(t.outputMode, "RGB") - i = ImageCms.applyTransform(lena(), t) - self.assert_image(i, "RGB", (128, 128)) - - # test PointTransform convenience API - lena().point(t) - - def test_name(self): - # get profile information for file - self.assertEqual( - ImageCms.getProfileName(SRGB).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') - - def test_info(self): - self.assertEqual( - ImageCms.getProfileInfo(SRGB).splitlines(), [ - 'sRGB IEC61966-2.1', '', - 'Copyright (c) 1998 Hewlett-Packard Company', '']) - - def test_copyright(self): - self.assertEqual( - ImageCms.getProfileCopyright(SRGB).strip(), - 'Copyright (c) 1998 Hewlett-Packard Company') - - def test_manufacturer(self): - self.assertEqual( - ImageCms.getProfileManufacturer(SRGB).strip(), - 'IEC http://www.iec.ch') - - def test_model(self): - self.assertEqual( - ImageCms.getProfileModel(SRGB).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') - - def test_description(self): - self.assertEqual( - ImageCms.getProfileDescription(SRGB).strip(), - 'sRGB IEC61966-2.1') - - def test_intent(self): - self.assertEqual(ImageCms.getDefaultIntent(SRGB), 0) - self.assertEqual(ImageCms.isIntentSupported( - SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) - - def test_profile_object(self): - # same, using profile object - p = ImageCms.createProfile("sRGB") - # self.assertEqual(ImageCms.getProfileName(p).strip(), - # 'sRGB built-in - (lcms internal)') - # self.assertEqual(ImageCms.getProfileInfo(p).splitlines(), - # ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) - self.assertEqual(ImageCms.getDefaultIntent(p), 0) - self.assertEqual(ImageCms.isIntentSupported( - p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) - - def test_extensions(self): - # extensions - from io import BytesIO - i = Image.open("Tests/images/rgb.jpg") - p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) - self.assertEqual( - ImageCms.getProfileName(p).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') - - def test_exceptions(self): - # the procedural pyCMS API uses PyCMSError for all sorts of errors - self.assertRaises( - ImageCms.PyCMSError, - lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) - self.assertRaises( - ImageCms.PyCMSError, - lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) - self.assertRaises( - ImageCms.PyCMSError, - lambda: ImageCms.getProfileName(None)) - self.assertRaises( - ImageCms.PyCMSError, - lambda: ImageCms.isIntentSupported(SRGB, None, None)) - - def test_display_profile(self): - # try fetching the profile for the current display device - ImageCms.get_display_profile() - - def test_lab_color_profile(self): - ImageCms.createProfile("LAB", 5000) - ImageCms.createProfile("LAB", 6500) - - def test_simple_lab(self): - i = Image.new('RGB', (10, 10), (128, 128, 128)) - - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - - i_lab = ImageCms.applyTransform(i, t) - - self.assertEqual(i_lab.mode, 'LAB') - - k = i_lab.getpixel((0, 0)) - # not a linear luminance map. so L != 128: - self.assertEqual(k, (137, 128, 128)) - - L = i_lab.getdata(0) - a = i_lab.getdata(1) - b = i_lab.getdata(2) - - self.assertEqual(list(L), [137] * 100) - self.assertEqual(list(a), [128] * 100) - self.assertEqual(list(b), [128] * 100) - - def test_lab_color(self): - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - # Need to add a type mapping for some PIL type to TYPE_Lab_8 in - # findLCMSType, and have that mapping work back to a PIL mode - # (likely RGB). - i = ImageCms.applyTransform(lena(), t) - self.assert_image(i, "LAB", (128, 128)) - - # i.save('temp.lab.tif') # visually verified vs PS. - - target = Image.open('Tests/images/lena.Lab.tif') - - self.assert_image_similar(i, target, 30) - - def test_lab_srgb(self): - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") - - img = Image.open('Tests/images/lena.Lab.tif') - - img_srgb = ImageCms.applyTransform(img, t) - - # img_srgb.save('temp.srgb.tif') # visually verified vs ps. - - self.assert_image_similar(lena(), img_srgb, 30) - - def test_lab_roundtrip(self): - # check to see if we're at least internally consistent. - pLab = ImageCms.createProfile("LAB") - t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") - - t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") - - i = ImageCms.applyTransform(lena(), t) - out = ImageCms.applyTransform(i, t2) - - self.assert_image_similar(lena(), out, 2) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagecolor.py b/test/test_imagecolor.py deleted file mode 100644 index 5d8944852..000000000 --- a/test/test_imagecolor.py +++ /dev/null @@ -1,71 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image -from PIL import ImageColor - - -class TestImageColor(PillowTestCase): - - def test_sanity(self): - self.assertEqual((255, 0, 0), ImageColor.getrgb("#f00")) - self.assertEqual((255, 0, 0), ImageColor.getrgb("#ff0000")) - self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) - self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)")) - self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) - self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) - self.assertEqual((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)")) - self.assertEqual( - (255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) - self.assertEqual((255, 0, 0), ImageColor.getrgb("red")) - - # look for rounding errors (based on code by Tim Hatch) - def test_rounding_errors(self): - - for color in list(ImageColor.colormap.keys()): - expected = Image.new( - "RGB", (1, 1), color).convert("L").getpixel((0, 0)) - actual = Image.new("L", (1, 1), color).getpixel((0, 0)) - self.assertEqual(expected, actual) - - self.assertEqual((0, 0, 0), ImageColor.getcolor("black", "RGB")) - self.assertEqual((255, 255, 255), ImageColor.getcolor("white", "RGB")) - self.assertEqual( - (0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB")) - Image.new("RGB", (1, 1), "white") - - self.assertEqual((0, 0, 0, 255), ImageColor.getcolor("black", "RGBA")) - self.assertEqual( - (255, 255, 255, 255), ImageColor.getcolor("white", "RGBA")) - self.assertEqual( - (0, 255, 115, 33), - ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGBA")) - Image.new("RGBA", (1, 1), "white") - - self.assertEqual(0, ImageColor.getcolor("black", "L")) - self.assertEqual(255, ImageColor.getcolor("white", "L")) - self.assertEqual( - 162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) - Image.new("L", (1, 1), "white") - - self.assertEqual(0, ImageColor.getcolor("black", "1")) - self.assertEqual(255, ImageColor.getcolor("white", "1")) - # The following test is wrong, but is current behavior - # The correct result should be 255 due to the mode 1 - self.assertEqual( - 162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) - # Correct behavior - # self.assertEqual( - # 255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) - Image.new("1", (1, 1), "white") - - self.assertEqual((0, 255), ImageColor.getcolor("black", "LA")) - self.assertEqual((255, 255), ImageColor.getcolor("white", "LA")) - self.assertEqual( - (162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) - Image.new("LA", (1, 1), "white") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagedraw.py b/test/test_imagedraw.py deleted file mode 100644 index 98876296f..000000000 --- a/test/test_imagedraw.py +++ /dev/null @@ -1,255 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image -from PIL import ImageColor -from PIL import ImageDraw - -import sys - -# Image size -w, h = 100, 100 - -# Bounding box points -x0 = int(w / 4) -x1 = int(x0 * 3) -y0 = int(h / 4) -y1 = int(x0 * 3) - -# Two kinds of bounding box -bbox1 = [(x0, y0), (x1, y1)] -bbox2 = [x0, y0, x1, y1] - -# Two kinds of coordinate sequences -points1 = [(10, 10), (20, 40), (30, 30)] -points2 = [10, 10, 20, 40, 30, 30] - - -class TestImageDraw(PillowTestCase): - - def test_sanity(self): - im = lena("RGB").copy() - - draw = ImageDraw.ImageDraw(im) - draw = ImageDraw.Draw(im) - - draw.ellipse(list(range(4))) - draw.line(list(range(10))) - draw.polygon(list(range(100))) - draw.rectangle(list(range(4))) - - def test_deprecated(self): - im = lena().copy() - - draw = ImageDraw.Draw(im) - - self.assert_warning(DeprecationWarning, lambda: draw.setink(0)) - self.assert_warning(DeprecationWarning, lambda: draw.setfill(0)) - - def helper_arc(self, bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - # FIXME Fill param should be named outline. - draw.arc(bbox, 0, 180) - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_arc.png")) - - def test_arc1(self): - self.helper_arc(bbox1) - - def test_arc2(self): - self.helper_arc(bbox2) - - def test_bitmap(self): - # Arrange - small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.bitmap((10, 10), small) - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_bitmap.png")) - - def helper_chord(self, bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.chord(bbox, 0, 180, fill="red", outline="yellow") - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_chord.png")) - - def test_chord1(self): - self.helper_chord(bbox1) - - def test_chord2(self): - self.helper_chord(bbox2) - - def helper_ellipse(self, bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.ellipse(bbox, fill="green", outline="blue") - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_ellipse.png")) - - def test_ellipse1(self): - self.helper_ellipse(bbox1) - - def test_ellipse2(self): - self.helper_ellipse(bbox2) - - def helper_line(self, points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.line(points1, fill="yellow", width=2) - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_line.png")) - - def test_line1(self): - self.helper_line(points1) - - def test_line2(self): - self.helper_line(points2) - - def helper_pieslice(self, bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.pieslice(bbox, -90, 45, fill="white", outline="blue") - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_pieslice.png")) - - def test_pieslice1(self): - self.helper_pieslice(bbox1) - - def test_pieslice2(self): - self.helper_pieslice(bbox2) - - def helper_point(self, points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.point(points1, fill="yellow") - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_point.png")) - - def test_point1(self): - self.helper_point(points1) - - def test_point2(self): - self.helper_point(points2) - - def helper_polygon(self, points): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.polygon(points1, fill="red", outline="blue") - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_polygon.png")) - - def test_polygon1(self): - self.helper_polygon(points1) - - def test_polygon2(self): - self.helper_polygon(points2) - - def helper_rectangle(self, bbox): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - - # Act - draw.rectangle(bbox, fill="black", outline="green") - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_rectangle.png")) - - def test_rectangle1(self): - self.helper_rectangle(bbox1) - - def test_rectangle2(self): - self.helper_rectangle(bbox2) - - def test_floodfill(self): - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - draw.rectangle(bbox2, outline="yellow", fill="green") - centre_point = (int(w/2), int(h/2)) - - # Act - ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_floodfill.png")) - - @unittest.skipIf(hasattr(sys, 'pypy_version_info'), - "Causes fatal RPython error on PyPy") - def test_floodfill_border(self): - # floodfill() is experimental - - # Arrange - im = Image.new("RGB", (w, h)) - draw = ImageDraw.Draw(im) - draw.rectangle(bbox2, outline="yellow", fill="green") - centre_point = (int(w/2), int(h/2)) - - # Act - ImageDraw.floodfill( - im, centre_point, ImageColor.getrgb("red"), - border=ImageColor.getrgb("black")) - del draw - - # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_floodfill2.png")) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imageenhance.py b/test/test_imageenhance.py deleted file mode 100644 index 433c49cf6..000000000 --- a/test/test_imageenhance.py +++ /dev/null @@ -1,28 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image -from PIL import ImageEnhance - - -class TestImageEnhance(PillowTestCase): - - def test_sanity(self): - - # FIXME: assert_image - # Implicit asserts no exception: - ImageEnhance.Color(lena()).enhance(0.5) - ImageEnhance.Contrast(lena()).enhance(0.5) - ImageEnhance.Brightness(lena()).enhance(0.5) - ImageEnhance.Sharpness(lena()).enhance(0.5) - - def test_crash(self): - - # crashes on small images - im = Image.new("RGB", (1, 1)) - ImageEnhance.Sharpness(im).enhance(0.5) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagefileio.py b/test/test_imagefileio.py deleted file mode 100644 index 32ee0bc5e..000000000 --- a/test/test_imagefileio.py +++ /dev/null @@ -1,33 +0,0 @@ -from helper import unittest, PillowTestCase, lena, tostring - -from PIL import Image -from PIL import ImageFileIO - - -class TestImageFileIo(PillowTestCase): - - def test_fileio(self): - - class DumbFile: - def __init__(self, data): - self.data = data - - def read(self, bytes=None): - assert(bytes is None) - return self.data - - def close(self): - pass - - im1 = lena() - - io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) - - im2 = Image.open(io) - self.assert_image_equal(im1, im2) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagefilter.py b/test/test_imagefilter.py deleted file mode 100644 index f7edb409a..000000000 --- a/test/test_imagefilter.py +++ /dev/null @@ -1,37 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import ImageFilter - - -class TestImageFilter(PillowTestCase): - - def test_sanity(self): - # see test_image_filter for more tests - - # Check these run. Exceptions cause failures. - ImageFilter.MaxFilter - ImageFilter.MedianFilter - ImageFilter.MinFilter - ImageFilter.ModeFilter - ImageFilter.Kernel((3, 3), list(range(9))) - ImageFilter.GaussianBlur - ImageFilter.GaussianBlur(5) - ImageFilter.UnsharpMask - ImageFilter.UnsharpMask(10) - - ImageFilter.BLUR - ImageFilter.CONTOUR - ImageFilter.DETAIL - ImageFilter.EDGE_ENHANCE - ImageFilter.EDGE_ENHANCE_MORE - ImageFilter.EMBOSS - ImageFilter.FIND_EDGES - ImageFilter.SMOOTH - ImageFilter.SMOOTH_MORE - ImageFilter.SHARPEN - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagefont.py b/test/test_imagefont.py deleted file mode 100644 index 17cb38cc2..000000000 --- a/test/test_imagefont.py +++ /dev/null @@ -1,145 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image -from PIL import ImageDraw -from io import BytesIO -import os - -font_path = "Tests/fonts/FreeMono.ttf" -font_size = 20 - - -try: - from PIL import ImageFont - ImageFont.core.getfont # check if freetype is available - - class TestImageFont(PillowTestCase): - - def test_sanity(self): - self.assertRegexpMatches( - ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") - - def test_font_with_name(self): - ImageFont.truetype(font_path, font_size) - self._render(font_path) - self._clean() - - def _font_as_bytes(self): - with open(font_path, 'rb') as f: - font_bytes = BytesIO(f.read()) - return font_bytes - - def test_font_with_filelike(self): - ImageFont.truetype(self._font_as_bytes(), font_size) - self._render(self._font_as_bytes()) - # Usage note: making two fonts from the same buffer fails. - # shared_bytes = self._font_as_bytes() - # self._render(shared_bytes) - # self.assertRaises(Exception, lambda: _render(shared_bytes)) - self._clean() - - def test_font_with_open_file(self): - with open(font_path, 'rb') as f: - self._render(f) - self._clean() - - def test_font_old_parameters(self): - self.assert_warning( - DeprecationWarning, - lambda: ImageFont.truetype(filename=font_path, size=font_size)) - - def _render(self, font): - txt = "Hello World!" - ttf = ImageFont.truetype(font, font_size) - w, h = ttf.getsize(txt) - img = Image.new("RGB", (256, 64), "white") - d = ImageDraw.Draw(img) - d.text((10, 10), txt, font=ttf, fill='black') - - img.save('font.png') - return img - - def _clean(self): - os.unlink('font.png') - - def test_render_equal(self): - img_path = self._render(font_path) - with open(font_path, 'rb') as f: - font_filelike = BytesIO(f.read()) - img_filelike = self._render(font_filelike) - - self.assert_image_equal(img_path, img_filelike) - self._clean() - - def test_render_multiline(self): - im = Image.new(mode='RGB', size=(300, 100)) - draw = ImageDraw.Draw(im) - ttf = ImageFont.truetype(font_path, font_size) - line_spacing = draw.textsize('A', font=ttf)[1] + 8 - lines = ['hey you', 'you are awesome', 'this looks awkward'] - y = 0 - for line in lines: - draw.text((0, y), line, font=ttf) - y += line_spacing - - target = 'Tests/images/multiline_text.png' - target_img = Image.open(target) - - # some versions of freetype have different horizontal spacing. - # setting a tight epsilon, I'm showing the original test failure - # at epsilon = ~38. - self.assert_image_similar(im, target_img, .5) - - def test_rotated_transposed_font(self): - img_grey = Image.new("L", (100, 100)) - draw = ImageDraw.Draw(img_grey) - word = "testing" - font = ImageFont.truetype(font_path, font_size) - - orientation = Image.ROTATE_90 - transposed_font = ImageFont.TransposedFont( - font, orientation=orientation) - - # Original font - draw.setfont(font) - box_size_a = draw.textsize(word) - - # Rotated font - draw.setfont(transposed_font) - box_size_b = draw.textsize(word) - - # 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[1], box_size_b[0]) - - def test_unrotated_transposed_font(self): - img_grey = Image.new("L", (100, 100)) - draw = ImageDraw.Draw(img_grey) - word = "testing" - font = ImageFont.truetype(font_path, font_size) - - orientation = None - transposed_font = ImageFont.TransposedFont( - font, orientation=orientation) - - # Original font - draw.setfont(font) - box_size_a = draw.textsize(word) - - # Rotated font - draw.setfont(transposed_font) - box_size_b = draw.textsize(word) - - # Check boxes a and b are same size - self.assertEqual(box_size_a, box_size_b) - -except ImportError: - class TestImageFont(PillowTestCase): - def test_skip(self): - self.skipTest("ImportError") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagegrab.py b/test/test_imagegrab.py deleted file mode 100644 index 2275d34a1..000000000 --- a/test/test_imagegrab.py +++ /dev/null @@ -1,25 +0,0 @@ -from helper import unittest, PillowTestCase - -try: - from PIL import ImageGrab - - class TestImageCopy(PillowTestCase): - - def test_grab(self): - im = ImageGrab.grab() - self.assert_image(im, im.mode, im.size) - - def test_grab2(self): - im = ImageGrab.grab() - self.assert_image(im, im.mode, im.size) - -except ImportError: - class TestImageCopy(PillowTestCase): - def test_skip(self): - self.skipTest("ImportError") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagemath.py b/test/test_imagemath.py deleted file mode 100644 index 17d43d25a..000000000 --- a/test/test_imagemath.py +++ /dev/null @@ -1,78 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image -from PIL import ImageMath - - -def pixel(im): - if hasattr(im, "im"): - return "%s %r" % (im.mode, im.getpixel((0, 0))) - else: - if isinstance(im, type(0)): - return int(im) # hack to deal with booleans - print(im) - -A = Image.new("L", (1, 1), 1) -B = Image.new("L", (1, 1), 2) -F = Image.new("F", (1, 1), 3) -I = Image.new("I", (1, 1), 4) - -images = {"A": A, "B": B, "F": F, "I": I} - - -class TestImageMath(PillowTestCase): - - def test_sanity(self): - self.assertEqual(ImageMath.eval("1"), 1) - self.assertEqual(ImageMath.eval("1+A", A=2), 3) - self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") - self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") - self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") - self.assertEqual(pixel( - ImageMath.eval("int(float(A)+B)", images)), "I 3") - - def test_ops(self): - - self.assertEqual(pixel(ImageMath.eval("-A", images)), "I -1") - self.assertEqual(pixel(ImageMath.eval("+B", images)), "L 2") - - self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") - self.assertEqual(pixel(ImageMath.eval("A-B", images)), "I -1") - self.assertEqual(pixel(ImageMath.eval("A*B", images)), "I 2") - self.assertEqual(pixel(ImageMath.eval("A/B", images)), "I 0") - self.assertEqual(pixel(ImageMath.eval("B**2", images)), "I 4") - self.assertEqual(pixel( - ImageMath.eval("B**33", images)), "I 2147483647") - - self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") - self.assertEqual(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") - self.assertEqual(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") - self.assertEqual(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") - self.assertEqual(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") - self.assertEqual(pixel( - ImageMath.eval("float(B)**33", images)), "F 8589934592.0") - - def test_logical(self): - self.assertEqual(pixel(ImageMath.eval("not A", images)), 0) - self.assertEqual(pixel(ImageMath.eval("A and B", images)), "L 2") - self.assertEqual(pixel(ImageMath.eval("A or B", images)), "L 1") - - def test_convert(self): - self.assertEqual(pixel( - ImageMath.eval("convert(A+B, 'L')", images)), "L 3") - self.assertEqual(pixel( - ImageMath.eval("convert(A+B, '1')", images)), "1 0") - self.assertEqual(pixel( - ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") - - def test_compare(self): - self.assertEqual(pixel(ImageMath.eval("min(A, B)", images)), "I 1") - self.assertEqual(pixel(ImageMath.eval("max(A, B)", images)), "I 2") - self.assertEqual(pixel(ImageMath.eval("A == 1", images)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A == 2", images)), "I 0") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagemode.py b/test/test_imagemode.py deleted file mode 100644 index 7fb596b46..000000000 --- a/test/test_imagemode.py +++ /dev/null @@ -1,32 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import ImageMode - - -class TestImage(PillowTestCase): - - def test_sanity(self): - ImageMode.getmode("1") - ImageMode.getmode("L") - ImageMode.getmode("P") - ImageMode.getmode("RGB") - ImageMode.getmode("I") - ImageMode.getmode("F") - - m = ImageMode.getmode("1") - self.assertEqual(m.mode, "1") - self.assertEqual(m.bands, ("1",)) - self.assertEqual(m.basemode, "L") - self.assertEqual(m.basetype, "L") - - m = ImageMode.getmode("RGB") - self.assertEqual(m.mode, "RGB") - self.assertEqual(m.bands, ("R", "G", "B")) - self.assertEqual(m.basemode, "RGB") - self.assertEqual(m.basetype, "L") - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imageops.py b/test/test_imageops.py deleted file mode 100644 index a4a94ca4d..000000000 --- a/test/test_imageops.py +++ /dev/null @@ -1,85 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import ImageOps - - -class TestImageOps(PillowTestCase): - - class Deformer: - def getmesh(self, im): - x, y = im.size - return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] - - deformer = Deformer() - - def test_sanity(self): - - ImageOps.autocontrast(lena("L")) - ImageOps.autocontrast(lena("RGB")) - - ImageOps.autocontrast(lena("L"), cutoff=10) - ImageOps.autocontrast(lena("L"), ignore=[0, 255]) - - ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) - ImageOps.colorize(lena("L"), "black", "white") - - ImageOps.crop(lena("L"), 1) - ImageOps.crop(lena("RGB"), 1) - - ImageOps.deform(lena("L"), self.deformer) - ImageOps.deform(lena("RGB"), self.deformer) - - ImageOps.equalize(lena("L")) - ImageOps.equalize(lena("RGB")) - - ImageOps.expand(lena("L"), 1) - ImageOps.expand(lena("RGB"), 1) - ImageOps.expand(lena("L"), 2, "blue") - ImageOps.expand(lena("RGB"), 2, "blue") - - ImageOps.fit(lena("L"), (128, 128)) - ImageOps.fit(lena("RGB"), (128, 128)) - - ImageOps.flip(lena("L")) - ImageOps.flip(lena("RGB")) - - ImageOps.grayscale(lena("L")) - ImageOps.grayscale(lena("RGB")) - - ImageOps.invert(lena("L")) - ImageOps.invert(lena("RGB")) - - ImageOps.mirror(lena("L")) - ImageOps.mirror(lena("RGB")) - - ImageOps.posterize(lena("L"), 4) - ImageOps.posterize(lena("RGB"), 4) - - ImageOps.solarize(lena("L")) - ImageOps.solarize(lena("RGB")) - - def test_1pxfit(self): - # Division by zero in equalize if image is 1 pixel high - newimg = ImageOps.fit(lena("RGB").resize((1, 1)), (35, 35)) - self.assertEqual(newimg.size, (35, 35)) - - newimg = ImageOps.fit(lena("RGB").resize((1, 100)), (35, 35)) - self.assertEqual(newimg.size, (35, 35)) - - newimg = ImageOps.fit(lena("RGB").resize((100, 1)), (35, 35)) - self.assertEqual(newimg.size, (35, 35)) - - def test_pil163(self): - # Division by zero in equalize if < 255 pixels in image (@PIL163) - - i = lena("RGB").resize((15, 16)) - - ImageOps.equalize(i.convert("L")) - ImageOps.equalize(i.convert("P")) - ImageOps.equalize(i.convert("RGB")) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imageshow.py b/test/test_imageshow.py deleted file mode 100644 index 12594c98f..000000000 --- a/test/test_imageshow.py +++ /dev/null @@ -1,18 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image -from PIL import ImageShow - - -class TestImage(PillowTestCase): - - def test_sanity(self): - dir(Image) - dir(ImageShow) - pass - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagestat.py b/test/test_imagestat.py deleted file mode 100644 index 4d30ff023..000000000 --- a/test/test_imagestat.py +++ /dev/null @@ -1,63 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image -from PIL import ImageStat - - -class TestImageStat(PillowTestCase): - - def test_sanity(self): - - im = lena() - - st = ImageStat.Stat(im) - st = ImageStat.Stat(im.histogram()) - st = ImageStat.Stat(im, Image.new("1", im.size, 1)) - - # Check these run. Exceptions will cause failures. - st.extrema - st.sum - st.mean - st.median - st.rms - st.sum2 - st.var - st.stddev - - self.assertRaises(AttributeError, lambda: st.spam) - - self.assertRaises(TypeError, lambda: ImageStat.Stat(1)) - - def test_lena(self): - - im = lena() - - st = ImageStat.Stat(im) - - # verify a few values - self.assertEqual(st.extrema[0], (61, 255)) - self.assertEqual(st.median[0], 197) - self.assertEqual(st.sum[0], 2954416) - self.assertEqual(st.sum[1], 2027250) - self.assertEqual(st.sum[2], 1727331) - - def test_constant(self): - - im = Image.new("L", (128, 128), 128) - - st = ImageStat.Stat(im) - - self.assertEqual(st.extrema[0], (128, 128)) - self.assertEqual(st.sum[0], 128**3) - self.assertEqual(st.sum2[0], 128**4) - self.assertEqual(st.mean[0], 128) - self.assertEqual(st.median[0], 128) - self.assertEqual(st.rms[0], 128) - self.assertEqual(st.var[0], 0) - self.assertEqual(st.stddev[0], 0) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagetk.py b/test/test_imagetk.py deleted file mode 100644 index 87a07e288..000000000 --- a/test/test_imagetk.py +++ /dev/null @@ -1,17 +0,0 @@ -from helper import unittest, PillowTestCase - - -class TestImageTk(PillowTestCase): - - def test_import(self): - try: - from PIL import ImageTk - dir(ImageTk) - except (OSError, ImportError) as v: - self.skipTest(v) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagetransform.py b/test/test_imagetransform.py deleted file mode 100644 index f5741df32..000000000 --- a/test/test_imagetransform.py +++ /dev/null @@ -1,27 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image -from PIL import ImageTransform - - -class TestImageTransform(PillowTestCase): - - def test_sanity(self): - im = Image.new("L", (100, 100)) - - seq = tuple(range(10)) - - transform = ImageTransform.AffineTransform(seq[:6]) - im.transform((100, 100), transform) - transform = ImageTransform.ExtentTransform(seq[:4]) - im.transform((100, 100), transform) - transform = ImageTransform.QuadTransform(seq[:8]) - im.transform((100, 100), transform) - transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) - im.transform((100, 100), transform) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_imagewin.py b/test/test_imagewin.py deleted file mode 100644 index 916abc77b..000000000 --- a/test/test_imagewin.py +++ /dev/null @@ -1,18 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image -from PIL import ImageWin - - -class TestImage(PillowTestCase): - - def test_sanity(self): - dir(Image) - dir(ImageWin) - pass - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_lib_image.py b/test/test_lib_image.py deleted file mode 100644 index e0a903b00..000000000 --- a/test/test_lib_image.py +++ /dev/null @@ -1,39 +0,0 @@ -from helper import unittest, PillowTestCase - -from PIL import Image - - -class TestSanity(PillowTestCase): - - def test_setmode(self): - - im = Image.new("L", (1, 1), 255) - im.im.setmode("1") - self.assertEqual(im.im.getpixel((0, 0)), 255) - im.im.setmode("L") - self.assertEqual(im.im.getpixel((0, 0)), 255) - - im = Image.new("1", (1, 1), 1) - im.im.setmode("L") - self.assertEqual(im.im.getpixel((0, 0)), 255) - im.im.setmode("1") - self.assertEqual(im.im.getpixel((0, 0)), 255) - - im = Image.new("RGB", (1, 1), (1, 2, 3)) - im.im.setmode("RGB") - self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3)) - im.im.setmode("RGBA") - self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3, 255)) - im.im.setmode("RGBX") - self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3, 255)) - im.im.setmode("RGB") - self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3)) - - self.assertRaises(ValueError, lambda: im.im.setmode("L")) - self.assertRaises(ValueError, lambda: im.im.setmode("RGBABCDE")) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_lib_pack.py b/test/test_lib_pack.py deleted file mode 100644 index 102835b58..000000000 --- a/test/test_lib_pack.py +++ /dev/null @@ -1,147 +0,0 @@ -from helper import unittest, PillowTestCase, py3 - -from PIL import Image - - -class TestLibPack(PillowTestCase): - - def pack(self): - pass # not yet - - def test_pack(self): - - def pack(mode, rawmode): - if len(mode) == 1: - im = Image.new(mode, (1, 1), 1) - else: - im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) - - if py3: - return list(im.tobytes("raw", rawmode)) - else: - return [ord(c) for c in im.tobytes("raw", rawmode)] - - order = 1 if Image._ENDIAN == '<' else -1 - - self.assertEqual(pack("1", "1"), [128]) - self.assertEqual(pack("1", "1;I"), [0]) - self.assertEqual(pack("1", "1;R"), [1]) - self.assertEqual(pack("1", "1;IR"), [0]) - - self.assertEqual(pack("L", "L"), [1]) - - self.assertEqual(pack("I", "I"), [1, 0, 0, 0][::order]) - - self.assertEqual(pack("F", "F"), [0, 0, 128, 63][::order]) - - self.assertEqual(pack("LA", "LA"), [1, 2]) - - self.assertEqual(pack("RGB", "RGB"), [1, 2, 3]) - self.assertEqual(pack("RGB", "RGB;L"), [1, 2, 3]) - self.assertEqual(pack("RGB", "BGR"), [3, 2, 1]) - self.assertEqual(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? - self.assertEqual(pack("RGB", "BGRX"), [3, 2, 1, 0]) - self.assertEqual(pack("RGB", "XRGB"), [0, 1, 2, 3]) - self.assertEqual(pack("RGB", "XBGR"), [0, 3, 2, 1]) - - self.assertEqual(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? - - self.assertEqual(pack("RGBA", "RGBA"), [1, 2, 3, 4]) - - self.assertEqual(pack("CMYK", "CMYK"), [1, 2, 3, 4]) - self.assertEqual(pack("YCbCr", "YCbCr"), [1, 2, 3]) - - def test_unpack(self): - - def unpack(mode, rawmode, bytes_): - im = None - - if py3: - data = bytes(range(1, bytes_+1)) - else: - data = ''.join(chr(i) for i in range(1, bytes_+1)) - - im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) - - return im.getpixel((0, 0)) - - def unpack_1(mode, rawmode, value): - assert mode == "1" - im = None - - if py3: - im = Image.frombytes( - mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) - else: - im = Image.frombytes( - mode, (8, 1), chr(value), "raw", rawmode, 0, 1) - - return tuple(im.getdata()) - - X = 255 - - self.assertEqual(unpack_1("1", "1", 1), (0, 0, 0, 0, 0, 0, 0, X)) - self.assertEqual(unpack_1("1", "1;I", 1), (X, X, X, X, X, X, X, 0)) - self.assertEqual(unpack_1("1", "1;R", 1), (X, 0, 0, 0, 0, 0, 0, 0)) - self.assertEqual(unpack_1("1", "1;IR", 1), (0, X, X, X, X, X, X, X)) - - self.assertEqual(unpack_1("1", "1", 170), (X, 0, X, 0, X, 0, X, 0)) - self.assertEqual(unpack_1("1", "1;I", 170), (0, X, 0, X, 0, X, 0, X)) - self.assertEqual(unpack_1("1", "1;R", 170), (0, X, 0, X, 0, X, 0, X)) - self.assertEqual(unpack_1("1", "1;IR", 170), (X, 0, X, 0, X, 0, X, 0)) - - self.assertEqual(unpack("L", "L;2", 1), 0) - self.assertEqual(unpack("L", "L;4", 1), 0) - self.assertEqual(unpack("L", "L", 1), 1) - self.assertEqual(unpack("L", "L;I", 1), 254) - self.assertEqual(unpack("L", "L;R", 1), 128) - self.assertEqual(unpack("L", "L;16", 2), 2) # little endian - self.assertEqual(unpack("L", "L;16B", 2), 1) # big endian - - self.assertEqual(unpack("LA", "LA", 2), (1, 2)) - self.assertEqual(unpack("LA", "LA;L", 2), (1, 2)) - - self.assertEqual(unpack("RGB", "RGB", 3), (1, 2, 3)) - self.assertEqual(unpack("RGB", "RGB;L", 3), (1, 2, 3)) - self.assertEqual(unpack("RGB", "RGB;R", 3), (128, 64, 192)) - self.assertEqual(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? - self.assertEqual(unpack("RGB", "BGR", 3), (3, 2, 1)) - self.assertEqual(unpack("RGB", "RGB;15", 2), (8, 131, 0)) - self.assertEqual(unpack("RGB", "BGR;15", 2), (0, 131, 8)) - self.assertEqual(unpack("RGB", "RGB;16", 2), (8, 64, 0)) - self.assertEqual(unpack("RGB", "BGR;16", 2), (0, 64, 8)) - self.assertEqual(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) - - self.assertEqual(unpack("RGB", "RGBX", 4), (1, 2, 3)) - self.assertEqual(unpack("RGB", "BGRX", 4), (3, 2, 1)) - self.assertEqual(unpack("RGB", "XRGB", 4), (2, 3, 4)) - self.assertEqual(unpack("RGB", "XBGR", 4), (4, 3, 2)) - - self.assertEqual(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) - self.assertEqual(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) - self.assertEqual(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) - self.assertEqual(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) - self.assertEqual(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) - self.assertEqual(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) - self.assertEqual(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) - - self.assertEqual(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? - self.assertEqual(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) - self.assertEqual(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) - self.assertEqual(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) - self.assertEqual(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) - self.assertEqual(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) - self.assertEqual(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) - - self.assertEqual(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) - self.assertEqual(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) - - self.assertRaises(ValueError, lambda: unpack("L", "L", 0)) - self.assertRaises(ValueError, lambda: unpack("RGB", "RGB", 2)) - self.assertRaises(ValueError, lambda: unpack("CMYK", "CMYK", 2)) - - -if __name__ == '__main__': - unittest.main() - -# End of file diff --git a/test/test_locale.py b/test/test_locale.py deleted file mode 100644 index 599e46266..000000000 --- a/test/test_locale.py +++ /dev/null @@ -1,39 +0,0 @@ -from helper import unittest, PillowTestCase, lena - -from PIL import Image - -import locale - -# ref https://github.com/python-pillow/Pillow/issues/272 -# on windows, in polish locale: - -# import locale -# print locale.setlocale(locale.LC_ALL, 'polish') -# import string -# print len(string.whitespace) -# print ord(string.whitespace[6]) - -# Polish_Poland.1250 -# 7 -# 160 - -# one of string.whitespace is not freely convertable into ascii. - -path = "Images/lena.jpg" - - -class TestLocale(PillowTestCase): - - def test_sanity(self): - Image.open(path) - try: - locale.setlocale(locale.LC_ALL, "polish") - except: - unittest.skip('Polish locale not available') - Image.open(path) - - -if __name__ == '__main__': - unittest.main() - -# End of file From 6a79d80374e0e1c14601c4e12724e4507d4cd834 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 10 Jun 2014 07:45:42 -0400 Subject: [PATCH 151/168] Revert "Merge pull request #5 from hugovk/unittest1merge" This reverts commit 0940f0b043bb16fc37dc9c34673cfaf1a813cc61, reversing changes made to 07aa1a56bbddd79128a81584eb0ec8f903c5192f. Conflicts: .travis.yml test/helper.py test/test_imagedraw.py --- PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index fe4f47295..e064ed9ef 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -522,7 +522,7 @@ class Image: """ Closes the file pointer, if possible. - This operation will destroy the image core and release its memory. + This operation will destroy the image core and release it's memory. The image data will be unusable afterward. This function is only required to close images that have not From e4fa84412206120cf71af55b9c3f84d685da65d4 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 10 Jun 2014 08:59:29 -0400 Subject: [PATCH 152/168] Update --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 71a42e315..40d8ed5cd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Use unittest for tests + [hugovk] + - ImageCms fixes [hugovk] From 5ceb3ca875f23fc1c58f0f4b32d48900260404f9 Mon Sep 17 00:00:00 2001 From: James Anderson Date: Mon, 9 Jun 2014 11:39:40 -0700 Subject: [PATCH 153/168] Issue #697. On MSVC 2012 builds is not required. --- libImaging/TiffDecode.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libImaging/TiffDecode.h b/libImaging/TiffDecode.h index 90fe3c9d4..70aa63628 100644 --- a/libImaging/TiffDecode.h +++ b/libImaging/TiffDecode.h @@ -13,11 +13,12 @@ #include #endif -#ifndef _UNISTD_H -#include +#ifndef _MSC_VER + #ifndef _UNISTD_H + #include + #endif #endif - #ifndef min #define min(x,y) (( x > y ) ? y : x ) #define max(x,y) (( x < y ) ? y : x ) From a8e02e093ad8bddb3f33dcbf7c322787f286def7 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 18 Jun 2014 16:08:16 -0700 Subject: [PATCH 154/168] We don't need unistd.h anymore on unix platforms either --- libImaging/TiffDecode.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libImaging/TiffDecode.h b/libImaging/TiffDecode.h index 70aa63628..67d6e82d1 100644 --- a/libImaging/TiffDecode.h +++ b/libImaging/TiffDecode.h @@ -13,12 +13,6 @@ #include #endif -#ifndef _MSC_VER - #ifndef _UNISTD_H - #include - #endif -#endif - #ifndef min #define min(x,y) (( x > y ) ? y : x ) #define max(x,y) (( x < y ) ? y : x ) From 05d8cb5b8fe3e3b89d877c8663b3367b6d939e12 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 18 Jun 2014 16:08:50 -0700 Subject: [PATCH 155/168] Don't carve out exceptions for py2.4 compilers anymore --- libImaging/TiffDecode.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libImaging/TiffDecode.h b/libImaging/TiffDecode.h index 67d6e82d1..46c940d1b 100644 --- a/libImaging/TiffDecode.h +++ b/libImaging/TiffDecode.h @@ -38,11 +38,10 @@ extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); -#if defined(_MSC_VER) && (_MSC_VER == 1310) -/* VS2003/py2.4 can't use varargs. Skipping trace for now.*/ -#define TRACE(args) -#else - +/* + Trace debugging + legacy, don't enable for python 3.x, unicode issues. +*/ /* #define VA_ARGS(...) __VA_ARGS__ @@ -51,8 +50,5 @@ extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); #define TRACE(args) -#endif /* _MSC_VER */ - - #endif From 6e79cc00ffa630752414d79c145e783f610e8ee0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 19 Jun 2014 05:55:34 -0700 Subject: [PATCH 156/168] test runner for installed versions of pillow --- test-installed.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100755 test-installed.py diff --git a/test-installed.py b/test-installed.py new file mode 100755 index 000000000..7f58f4966 --- /dev/null +++ b/test-installed.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +import nose +import os +import sys +import glob + +# monkey with the path, removing the local directory but adding the Tests/ directory +# for helper.py and the other local imports there. + +del(sys.path[0]) +sys.path.insert(0, os.path.abspath('./Tests')) + +# if there's no test selected (mostly) choose a working default. +# Something is required, because if we import the tests from the local +# directory, once again, we've got the non-installed PIL in the way +if len(sys.argv) == 1: + sys.argv.extend(glob.glob('Tests/test*.py')) + +# Make sure that nose doesn't muck with our paths. +if ('--no-path-adjustment' not in sys.argv) and ('-P' not in sys.argv): + sys.argv.insert(1, '--no-path-adjustment') + +if __name__ == '__main__': + nose.main() From 0b0ec8b40faba37bbd937391aaa0cb1ef294ce9f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 19 Jun 2014 06:06:23 -0700 Subject: [PATCH 157/168] Proper skipping of tests when lcms2 is not installed --- Tests/test_imagecms.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index d4432d9be..f3f0791e5 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -18,6 +18,8 @@ class TestImageCms(PillowTestCase): def setUp(self): try: from PIL import ImageCms + # need to hit getattr to trigger the delayed import error + ImageCms.core.profile_open except ImportError as v: self.skipTest(v) From df3993a67748a9fb57eae41cd8ee0944fdf1f491 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 15:38:49 -0400 Subject: [PATCH 158/168] Update --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 40d8ed5cd..563c1abd3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Remove unistd.h #include for all platforms + [wiredfool] + - Use unittest for tests [hugovk] From 9f9a9f9e61e306edf01d00b0a55843db25741846 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 15:40:02 -0400 Subject: [PATCH 159/168] Update --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 563c1abd3..ff5a9ecb9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.5.0 (unreleased) ------------------ +- Support OpenJpeg 2.1 + [wiredfool] + - Remove unistd.h #include for all platforms [wiredfool] From 9ed5b08cb9dd4ae2748b12bc58e8d9b4c9f57b43 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 16:30:41 -0400 Subject: [PATCH 160/168] Update URL http://www.graficaobscura.com/interp/index.html --- PIL/ImageEnhance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index 10433343e..f802dc1d3 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -6,7 +6,7 @@ # # For a background, see "Image Processing By Interpolation and # Extrapolation", Paul Haeberli and Douglas Voorhies. Available -# at http://www.sgi.com/grafica/interp/index.html +# at http://www.graficaobscura.com/interp/index.html # # History: # 1996-03-23 fl Created From 175d68aeeb5277714044b2685064c135b4b7ec3c Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 16:44:52 -0400 Subject: [PATCH 161/168] Update URL; fix typo --- PIL/WalImageFile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index a962e8a99..d494bfd58 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -14,11 +14,11 @@ # # NOTE: This format cannot be automatically recognized, so the reader -# is not registered for use with Image.open(). To open a WEL file, use +# is not registered for use with Image.open(). To open a WAL file, use # the WalImageFile.open() function instead. # This reader is based on the specification available from: -# http://www.flipcode.com/tutorials/tut_q2levels.shtml +# http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml # and has been tested with a few sample files found using google. from __future__ import print_function From 4470b07c3fee3bd58e60dce20a1a7ca71f23da7f Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 20:10:55 -0400 Subject: [PATCH 162/168] Rename and rst-i-fy --- Scripts/{README => README.rst} | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) rename Scripts/{README => README.rst} (99%) diff --git a/Scripts/README b/Scripts/README.rst similarity index 99% rename from Scripts/README rename to Scripts/README.rst index befa7e7be..1b26ea68c 100644 --- a/Scripts/README +++ b/Scripts/README.rst @@ -1,6 +1,5 @@ -------- Scripts -------- +======= This directory contains a number of more or less trivial utilities and demo programs. @@ -9,50 +8,50 @@ Comments and contributions are welcome. --------------------------------------------------------------------- pildriver.py (by Eric S. Raymond) +-------------------------------------------------------------------- A class implementing an image-processing calculator for scripts. Parses lists of commnds (or, called interactively, command-line arguments) into image loads, transformations, and saves. --------------------------------------------------------------------- viewer.py +-------------------------------------------------------------------- A simple image viewer. Can display all file formats handled by PIL. Transparent images are properly handled. --------------------------------------------------------------------- thresholder.py +-------------------------------------------------------------------- A simple utility that demonstrates how a transparent 1-bit overlay can be used to show the current thresholding of an 8-bit image. --------------------------------------------------------------------- enhancer.py +-------------------------------------------------------------------- Illustrates the ImageEnhance module. Drag the sliders to modify the images. This might be very slow on some platforms, depending on the Tk version. --------------------------------------------------------------------- painter.py +-------------------------------------------------------------------- Illustrates how a painting program could be based on PIL and Tk. Press the left mouse button and drag over the image to remove the colour. Some clever tricks have been used to get decent performance when updating the screen; see the sources for details. --------------------------------------------------------------------- player.py +-------------------------------------------------------------------- A simple image sequence player. You can use either a sequence format like FLI/FLC, GIF, or ARG, or give a number of images which are interpreted as frames in a sequence. All frames must have the same size. --------------------------------------------------------------------- gifmaker.py +-------------------------------------------------------------------- Convert a sequence file to a GIF animation. @@ -60,20 +59,20 @@ Note that the GIF encoder provided with this release of PIL writes uncompressed GIF files only, so the resulting animations are rather large compared with these created by other tools. --------------------------------------------------------------------- explode.py +-------------------------------------------------------------------- Split a sequence file into individual frames. --------------------------------------------------------------------- image2py.py +-------------------------------------------------------------------- Convert an image to a Python module containing an IMAGE variable. Note that the module using the module must include JPEG and ZIP decoders, unless the -u option is used. --------------------------------------------------------------------- olesummary.py +-------------------------------------------------------------------- Uses the OleFileIO module to dump the summary information from an OLE structured storage file. This works with most OLE files, including From 562c14dcff0d199cb4e5af9550ce3ee3dd0ebef4 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 20:11:59 -0400 Subject: [PATCH 163/168] Rename and rst-i-fy --- Sane/{README => README.rst} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename Sane/{README => README.rst} (78%) diff --git a/Sane/README b/Sane/README.rst similarity index 78% rename from Sane/README rename to Sane/README.rst index fa6c8a05f..173934040 100644 --- a/Sane/README +++ b/Sane/README.rst @@ -1,5 +1,5 @@ - Python SANE module V1.1 (30 Sep. 2004) +================================================================================ The SANE module provides an interface to the SANE scanner and frame grabber interface for Linux. This module was contributed by Andrew @@ -9,11 +9,11 @@ word 'SANE' or 'sane' in the subject of your mail, otherwise it might be classified as spam in the future. -To build this module, type (in the Sane directory): +To build this module, type (in the Sane directory):: python setup.py build -In order to install the module type: +In order to install the module type:: python setup.py install From 5ebe01fd2d504219150678a7ce465aa1c242046a Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 20:38:55 -0400 Subject: [PATCH 164/168] Combine *.txt -> README.rst; rst-i-fy --- Tk/README.rst | 291 +++++++++++++++++++++++++++++++++++++++++++++++ Tk/booster.txt | 108 ------------------ Tk/install.txt | 41 ------- Tk/pilbitmap.txt | 149 ------------------------ 4 files changed, 291 insertions(+), 298 deletions(-) create mode 100644 Tk/README.rst delete mode 100644 Tk/booster.txt delete mode 100644 Tk/install.txt delete mode 100644 Tk/pilbitmap.txt diff --git a/Tk/README.rst b/Tk/README.rst new file mode 100644 index 000000000..904762d00 --- /dev/null +++ b/Tk/README.rst @@ -0,0 +1,291 @@ +Using PIL With Tkinter +==================================================================== + +Starting with 1.0 final (release candidate 2 and later, to be +precise), PIL can attach itself to Tkinter in flight. As a result, +you no longer need to rebuild the Tkinter extension to be able to +use PIL. + +However, if you cannot get the this to work on your platform, you +can do it in the old way: + +* Adding Tkinter support + +1. Compile Python's _tkinter.c with the WITH_APPINIT and WITH_PIL + flags set, and link it with tkImaging.c and tkappinit.c. To + do this, copy the former to the Modules directory, and edit + the _tkinter line in Setup (or Setup.in) according to the + instructions in that file. + + NOTE: if you have an old Python version, the tkappinit.c + file is not included by default. If this is the case, you + will have to add the following lines to tkappinit.c, after + the MOREBUTTONS stuff:: + + { + extern void TkImaging_Init(Tcl_Interp* interp); + TkImaging_Init(interp); + } + + This registers a Tcl command called "PyImagingPhoto", which is + use to communicate between PIL and Tk's PhotoImage handler. + + You must also change the _tkinter line in Setup (or Setup.in) + to something like:: + + _tkinter _tkinter.c tkImaging.c tkappinit.c -DWITH_APPINIT + -I/usr/local/include -L/usr/local/lib -ltk8.0 -ltcl8.0 -lX11 + +The Photoimage Booster Patch (for Windows 95/NT) +==================================================================== + + This patch kit boosts performance for 16/24-bit displays. The +first patch is required on Tk 4.2 (where it fixes the problems for +16-bit displays) and later versions, with the exception for Tk 8.0b1 +where Sun added something similar themselves, only to remove it in +8.0b2. By installing both patches, Tk's PhotoImage handling becomes +much faster on both 16-bit and 24-bit displays. The patch has been +tested with Tk 4.2 and 8.0. + + Here's a benchmark, made with a sample program which loads two +512x512 greyscale PGM's, and two 512x512 colour PPM's, and displays +each of them in a separate toplevel windows. Tcl/Tk was compiled +with Visual C 4.0, and run on a P100 under Win95. Image load times +are not included in the timings: + ++----------------------+------------+-------------+----------------+ +| | **8-bit** | **16-bit** | **24-bit** | ++----------------------+------------+-------------+----------------+ +| 1. original 4.2 code | 5.52 s | 8.57 s | 3.79 s | ++----------------------+------------+-------------+----------------+ +| 2. booster patch | 5.49 s | 1.87 s | 1.82 s | ++----------------------+------------+-------------+----------------+ +| speedup | None | 4.6x | 2.1x | ++----------------------+------------+-------------+----------------+ + +Here's the patches: + +1. For portability and speed, the best thing under Windows is to +treat 16-bit displays as if they were 24-bit. The Windows device +drivers take care of the rest. + + ---------------------------------------------------------------- + If you have Tk 4.1 or Tk 8.0b1, you don't have to apply this + patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2. + ---------------------------------------------------------------- + +In win/tkWinImage.c, change the following line in XCreateImage: + + imagePtr->bits_per_pixel = depth; + +to:: + + /* ==================================================================== */ + /* The tk photo image booster patch -- patch section 1 */ + /* ==================================================================== */ + + if (visual->class == TrueColor) + /* true colour is stored as 3 bytes: (blue, green, red) */ + imagePtr->bits_per_pixel = 24; + else + imagePtr->bits_per_pixel = depth; + + /* ==================================================================== */ + + +2. The DitherInstance implementation is not good. It's especially +bad on highend truecolour displays. IMO, it should be rewritten from +scratch (some other day...). + + Anyway, the following band-aid makes the situation a little bit +better under Windows. This hack trades some marginal quality (no +dithering on 16-bit displays) for a dramatic performance boost. +Requires patch 1, unless you're using Tk 4.1 or Tk 8.0b1. + +In generic/tkImgPhoto.c, add the #ifdef section to the DitherInstance +function:: + + /* ==================================================================== */ + + for (; height > 0; height -= nLines) { + if (nLines > height) { + nLines = height; + } + dstLinePtr = (unsigned char *) imagePtr->data; + yEnd = yStart + nLines; + + /* ==================================================================== */ + /* The tk photo image booster patch -- patch section 2 */ + /* ==================================================================== */ + + #ifdef __WIN32__ + if (colorPtr->visualInfo.class == TrueColor + && instancePtr->gamma == 1.0) { + /* Windows hicolor/truecolor booster */ + for (y = yStart; y < yEnd; ++y) { + destBytePtr = dstLinePtr; + srcPtr = srcLinePtr; + for (x = xStart; x < xEnd; ++x) { + destBytePtr[0] = srcPtr[2]; + destBytePtr[1] = srcPtr[1]; + destBytePtr[2] = srcPtr[0]; + destBytePtr += 3; srcPtr += 3; + } + srcLinePtr += lineLength; + dstLinePtr += bytesPerLine; + } + } else + #endif + + /* ==================================================================== */ + + for (y = yStart; y < yEnd; ++y) { + srcPtr = srcLinePtr; + errPtr = errLinePtr; + destBytePtr = dstLinePtr; + +last updated: 97-07-03/fl + + +The PIL Bitmap Booster Patch +==================================================================== + +The pilbitmap booster patch greatly improves performance of the +ImageTk.BitmapImage constructor. Unfortunately, the design of Tk +doesn't allow us to do this from the tkImaging interface module, so +you have to patch the Tk sources. + +Once installed, the ImageTk module will automatically detect this +patch. + +(Note: this patch has been tested with Tk 8.0 on Win32 only, but it +should work just fine on other platforms as well). + +1. To the beginning of TkGetBitmapData (in generic/tkImgBmap.c), add + the following stuff:: + + /* ==================================================================== */ + + int width, height, numBytes, hotX, hotY; + char *p, *end, *expandedFileName; + ParseInfo pi; + char *data = NULL; + Tcl_DString buffer; + + /* ==================================================================== */ + /* The pilbitmap booster patch -- patch section */ + /* ==================================================================== */ + + char *PILGetBitmapData(); + + if (string) { + /* Is this a PIL bitmap reference? */ + data = PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr); + if (data) + return data; + } + + /* ==================================================================== */ + + pi.string = string; + if (string == NULL) { + if (Tcl_IsSafe(interp)) { + +2. Append the following to the same file (you may wish to include +Imaging.h instead of copying the struct declaration...):: + + /* ==================================================================== */ + /* The pilbitmap booster patch -- code section */ + /* ==================================================================== */ + + /* Imaging declaration boldly copied from Imaging.h (!) */ + + typedef struct ImagingInstance *Imaging; /* a.k.a. ImagingImage :-) */ + + typedef unsigned char UINT8; + typedef int INT32; + + struct ImagingInstance { + + /* Format */ + char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */ + int type; /* Always 0 in this version */ + int depth; /* Always 8 in this version */ + int bands; /* Number of bands (1, 3, or 4) */ + int xsize; /* Image dimension. */ + int ysize; + + /* Colour palette (for "P" images only) */ + void* palette; + + /* Data pointers */ + UINT8 **image8; /* Set for 8-bit image (pixelsize=1). */ + INT32 **image32; /* Set for 32-bit image (pixelsize=4). */ + + /* Internals */ + char **image; /* Actual raster data. */ + char *block; /* Set if data is allocated in a single block. */ + + int pixelsize; /* Size of a pixel, in bytes (1 or 4) */ + int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ + + /* Virtual methods */ + void (*im_delete)(Imaging *); + + }; + + /* The pilbitmap booster patch allows you to pass PIL images to the + Tk bitmap decoder. Passing images this way is much more efficient + than using the "tobitmap" method. */ + + char * + PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr) + char *string; + int *widthPtr, *heightPtr; + int *hotXPtr, *hotYPtr; + { + char* data; + char* p; + int y; + Imaging im; + + if (strncmp(string, "PIL:", 4) != 0) + return NULL; + + im = (Imaging) atol(string + 4); + + if (strcmp(im->mode, "1") != 0 && strcmp(im->mode, "L") != 0) + return NULL; + + data = p = (char *) ckalloc((unsigned) ((im->xsize+7)/8) * im->ysize); + + for (y = 0; y < im->ysize; y++) { + char* in = im->image8[y]; + int i, m, b; + b = 0; m = 1; + for (i = 0; i < im->xsize; i++) { + if (in[i] != 0) + b |= m; + m <<= 1; + if (m == 256){ + *p++ = b; + b = 0; m = 1; + } + } + if (m != 1) + *p++ = b; + } + + *widthPtr = im->xsize; + *heightPtr = im->ysize; + *hotXPtr = -1; + *hotYPtr = -1; + + return data; + } + + /* ==================================================================== */ + +3. Recompile Tk and relink the _tkinter module (where necessary). + +Last updated: 97-05-17/fl diff --git a/Tk/booster.txt b/Tk/booster.txt deleted file mode 100644 index 2d787983b..000000000 --- a/Tk/booster.txt +++ /dev/null @@ -1,108 +0,0 @@ -==================================================================== -The Photoimage Booster Patch (for Windows 95/NT) -==================================================================== - - This patch kit boosts performance for 16/24-bit displays. The -first patch is required on Tk 4.2 (where it fixes the problems for -16-bit displays) and later versions, with the exception for Tk 8.0b1 -where Sun added something similar themselves, only to remove it in -8.0b2. By installing both patches, Tk's PhotoImage handling becomes -much faster on both 16-bit and 24-bit displays. The patch has been -tested with Tk 4.2 and 8.0. - - Here's a benchmark, made with a sample program which loads two -512x512 greyscale PGM's, and two 512x512 colour PPM's, and displays -each of them in a separate toplevel windows. Tcl/Tk was compiled -with Visual C 4.0, and run on a P100 under Win95. Image load times -are not included in the timings: - - 8-bit 16-bit 24-bit --------------------------------------------------------------------- -1. original 4.2 code 5.52 s 8.57 s 3.79 s -2. booster patch 5.49 s 1.87 s 1.82 s - - speedup None 4.6x 2.1x - -==================================================================== - -Here's the patches: - -1. For portability and speed, the best thing under Windows is to -treat 16-bit displays as if they were 24-bit. The Windows device -drivers take care of the rest. - - ---------------------------------------------------------------- - If you have Tk 4.1 or Tk 8.0b1, you don't have to apply this - patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2. - ---------------------------------------------------------------- - -In win/tkWinImage.c, change the following line in XCreateImage: - - imagePtr->bits_per_pixel = depth; - -to - -/* ==================================================================== */ -/* The tk photo image booster patch -- patch section 1 */ -/* ==================================================================== */ - - if (visual->class == TrueColor) - /* true colour is stored as 3 bytes: (blue, green, red) */ - imagePtr->bits_per_pixel = 24; - else - imagePtr->bits_per_pixel = depth; - -/* ==================================================================== */ - - -2. The DitherInstance implementation is not good. It's especially -bad on highend truecolour displays. IMO, it should be rewritten from -scratch (some other day...). - - Anyway, the following band-aid makes the situation a little bit -better under Windows. This hack trades some marginal quality (no -dithering on 16-bit displays) for a dramatic performance boost. -Requires patch 1, unless you're using Tk 4.1 or Tk 8.0b1. - -In generic/tkImgPhoto.c, add the #ifdef section to the DitherInstance -function: - - for (; height > 0; height -= nLines) { - if (nLines > height) { - nLines = height; - } - dstLinePtr = (unsigned char *) imagePtr->data; - yEnd = yStart + nLines; - -/* ==================================================================== */ -/* The tk photo image booster patch -- patch section 2 */ -/* ==================================================================== */ - -#ifdef __WIN32__ - if (colorPtr->visualInfo.class == TrueColor - && instancePtr->gamma == 1.0) { - /* Windows hicolor/truecolor booster */ - for (y = yStart; y < yEnd; ++y) { - destBytePtr = dstLinePtr; - srcPtr = srcLinePtr; - for (x = xStart; x < xEnd; ++x) { - destBytePtr[0] = srcPtr[2]; - destBytePtr[1] = srcPtr[1]; - destBytePtr[2] = srcPtr[0]; - destBytePtr += 3; srcPtr += 3; - } - srcLinePtr += lineLength; - dstLinePtr += bytesPerLine; - } - } else -#endif - -/* ==================================================================== */ - - for (y = yStart; y < yEnd; ++y) { - srcPtr = srcLinePtr; - errPtr = errLinePtr; - destBytePtr = dstLinePtr; - -==================================================================== -last updated: 97-07-03/fl diff --git a/Tk/install.txt b/Tk/install.txt deleted file mode 100644 index 0e2ade06d..000000000 --- a/Tk/install.txt +++ /dev/null @@ -1,41 +0,0 @@ -==================================================================== -Using PIL With Tkinter -==================================================================== - -Starting with 1.0 final (release candidate 2 and later, to be -precise), PIL can attach itself to Tkinter in flight. As a result, -you no longer need to rebuild the Tkinter extension to be able to -use PIL. - -However, if you cannot get the this to work on your platform, you -can do it in the old way: - -* Adding Tkinter support - -1. Compile Python's _tkinter.c with the WITH_APPINIT and WITH_PIL - flags set, and link it with tkImaging.c and tkappinit.c. To - do this, copy the former to the Modules directory, and edit - the _tkinter line in Setup (or Setup.in) according to the - instructions in that file. - - NOTE: if you have an old Python version, the tkappinit.c - file is not included by default. If this is the case, you - will have to add the following lines to tkappinit.c, after - the MOREBUTTONS stuff: - - { - extern void TkImaging_Init(Tcl_Interp* interp); - TkImaging_Init(interp); - } - - This registers a Tcl command called "PyImagingPhoto", which is - use to communicate between PIL and Tk's PhotoImage handler. - - You must also change the _tkinter line in Setup (or Setup.in) - to something like: - - _tkinter _tkinter.c tkImaging.c tkappinit.c -DWITH_APPINIT - -I/usr/local/include -L/usr/local/lib -ltk8.0 -ltcl8.0 -lX11 - - - diff --git a/Tk/pilbitmap.txt b/Tk/pilbitmap.txt deleted file mode 100644 index e7c46c65f..000000000 --- a/Tk/pilbitmap.txt +++ /dev/null @@ -1,149 +0,0 @@ -==================================================================== -The PIL Bitmap Booster Patch -==================================================================== - -The pilbitmap booster patch greatly improves performance of the -ImageTk.BitmapImage constructor. Unfortunately, the design of Tk -doesn't allow us to do this from the tkImaging interface module, so -you have to patch the Tk sources. - -Once installed, the ImageTk module will automatically detect this -patch. - -(Note: this patch has been tested with Tk 8.0 on Win32 only, but it -should work just fine on other platforms as well). - -1. To the beginning of TkGetBitmapData (in generic/tkImgBmap.c), add - the following stuff: - ------------------------------------------------------------------------- - int width, height, numBytes, hotX, hotY; - char *p, *end, *expandedFileName; - ParseInfo pi; - char *data = NULL; - Tcl_DString buffer; - -/* ==================================================================== */ -/* The pilbitmap booster patch -- patch section */ -/* ==================================================================== */ - - char *PILGetBitmapData(); - - if (string) { - /* Is this a PIL bitmap reference? */ - data = PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr); - if (data) - return data; - } - -/* ==================================================================== */ - - pi.string = string; - if (string == NULL) { - if (Tcl_IsSafe(interp)) { ------------------------------------------------------------------------- - - -2. Append the following to the same file (you may wish to include -Imaging.h instead of copying the struct declaration...) - ------------------------------------------------------------------------- - -/* ==================================================================== */ -/* The pilbitmap booster patch -- code section */ -/* ==================================================================== */ - -/* Imaging declaration boldly copied from Imaging.h (!) */ - -typedef struct ImagingInstance *Imaging; /* a.k.a. ImagingImage :-) */ - -typedef unsigned char UINT8; -typedef int INT32; - -struct ImagingInstance { - - /* Format */ - char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */ - int type; /* Always 0 in this version */ - int depth; /* Always 8 in this version */ - int bands; /* Number of bands (1, 3, or 4) */ - int xsize; /* Image dimension. */ - int ysize; - - /* Colour palette (for "P" images only) */ - void* palette; - - /* Data pointers */ - UINT8 **image8; /* Set for 8-bit image (pixelsize=1). */ - INT32 **image32; /* Set for 32-bit image (pixelsize=4). */ - - /* Internals */ - char **image; /* Actual raster data. */ - char *block; /* Set if data is allocated in a single block. */ - - int pixelsize; /* Size of a pixel, in bytes (1 or 4) */ - int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ - - /* Virtual methods */ - void (*im_delete)(Imaging *); - -}; - -/* The pilbitmap booster patch allows you to pass PIL images to the - Tk bitmap decoder. Passing images this way is much more efficient - than using the "tobitmap" method. */ - -char * -PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr) - char *string; - int *widthPtr, *heightPtr; - int *hotXPtr, *hotYPtr; -{ - char* data; - char* p; - int y; - Imaging im; - - if (strncmp(string, "PIL:", 4) != 0) - return NULL; - - im = (Imaging) atol(string + 4); - - if (strcmp(im->mode, "1") != 0 && strcmp(im->mode, "L") != 0) - return NULL; - - data = p = (char *) ckalloc((unsigned) ((im->xsize+7)/8) * im->ysize); - - for (y = 0; y < im->ysize; y++) { - char* in = im->image8[y]; - int i, m, b; - b = 0; m = 1; - for (i = 0; i < im->xsize; i++) { - if (in[i] != 0) - b |= m; - m <<= 1; - if (m == 256){ - *p++ = b; - b = 0; m = 1; - } - } - if (m != 1) - *p++ = b; - } - - *widthPtr = im->xsize; - *heightPtr = im->ysize; - *hotXPtr = -1; - *hotYPtr = -1; - - return data; -} - -/* ==================================================================== */ - ------------------------------------------------------------------------- - -3. Recompile Tk and relink the _tkinter module (where necessary). - -==================================================================== -Last updated: 97-05-17/fl From 409ada85056decf8471835cf9f103b0361202425 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 20:44:53 -0400 Subject: [PATCH 165/168] Clean up --- Tk/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tk/README.rst b/Tk/README.rst index 904762d00..b379add17 100644 --- a/Tk/README.rst +++ b/Tk/README.rst @@ -9,7 +9,7 @@ use PIL. However, if you cannot get the this to work on your platform, you can do it in the old way: -* Adding Tkinter support +Adding Tkinter support 1. Compile Python's _tkinter.c with the WITH_APPINIT and WITH_PIL flags set, and link it with tkImaging.c and tkappinit.c. To @@ -74,7 +74,7 @@ drivers take care of the rest. patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2. ---------------------------------------------------------------- -In win/tkWinImage.c, change the following line in XCreateImage: +In win/tkWinImage.c, change the following line in XCreateImage:: imagePtr->bits_per_pixel = depth; From d1130b4d09da28e6fbac321323c4540f1a2ce44b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 20:48:00 -0400 Subject: [PATCH 166/168] Clean up --- Tk/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tk/README.rst b/Tk/README.rst index b379add17..d49a02843 100644 --- a/Tk/README.rst +++ b/Tk/README.rst @@ -69,10 +69,10 @@ Here's the patches: treat 16-bit displays as if they were 24-bit. The Windows device drivers take care of the rest. - ---------------------------------------------------------------- +.. Note:: + If you have Tk 4.1 or Tk 8.0b1, you don't have to apply this patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2. - ---------------------------------------------------------------- In win/tkWinImage.c, change the following line in XCreateImage:: From 4b2bc94b198209efcf08e0ce9ef60a6883f344ac Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 20:48:49 -0400 Subject: [PATCH 167/168] Clean up --- Tk/README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tk/README.rst b/Tk/README.rst index d49a02843..1fff4d314 100644 --- a/Tk/README.rst +++ b/Tk/README.rst @@ -39,7 +39,7 @@ Adding Tkinter support The Photoimage Booster Patch (for Windows 95/NT) ==================================================================== - This patch kit boosts performance for 16/24-bit displays. The +This patch kit boosts performance for 16/24-bit displays. The first patch is required on Tk 4.2 (where it fixes the problems for 16-bit displays) and later versions, with the exception for Tk 8.0b1 where Sun added something similar themselves, only to remove it in @@ -47,7 +47,7 @@ where Sun added something similar themselves, only to remove it in much faster on both 16-bit and 24-bit displays. The patch has been tested with Tk 4.2 and 8.0. - Here's a benchmark, made with a sample program which loads two +Here's a benchmark, made with a sample program which loads two 512x512 greyscale PGM's, and two 512x512 colour PPM's, and displays each of them in a separate toplevel windows. Tcl/Tk was compiled with Visual C 4.0, and run on a P100 under Win95. Image load times @@ -97,7 +97,7 @@ to:: bad on highend truecolour displays. IMO, it should be rewritten from scratch (some other day...). - Anyway, the following band-aid makes the situation a little bit +Anyway, the following band-aid makes the situation a little bit better under Windows. This hack trades some marginal quality (no dithering on 16-bit displays) for a dramatic performance boost. Requires patch 1, unless you're using Tk 4.1 or Tk 8.0b1. From 0cbf318004ea7fc462266739a6a2a82da106a73b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 22 Jun 2014 20:49:42 -0400 Subject: [PATCH 168/168] Update --- Tk/README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tk/README.rst b/Tk/README.rst index 1fff4d314..bb5eb9c3c 100644 --- a/Tk/README.rst +++ b/Tk/README.rst @@ -10,6 +10,7 @@ However, if you cannot get the this to work on your platform, you can do it in the old way: Adding Tkinter support +---------------------- 1. Compile Python's _tkinter.c with the WITH_APPINIT and WITH_PIL flags set, and link it with tkImaging.c and tkappinit.c. To @@ -74,7 +75,7 @@ drivers take care of the rest. If you have Tk 4.1 or Tk 8.0b1, you don't have to apply this patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2. -In win/tkWinImage.c, change the following line in XCreateImage:: +In ``win/tkWinImage.c``, change the following line in ``XCreateImage``:: imagePtr->bits_per_pixel = depth;