Merge branch 'master' into jpeg_qtables
							
								
								
									
										15
									
								
								CHANGES.rst
									
									
									
									
									
								
							
							
						
						|  | @ -4,6 +4,21 @@ Changelog (Pillow) | ||||||
| 2.5.0 (unreleased) | 2.5.0 (unreleased) | ||||||
| ------------------ | ------------------ | ||||||
| 
 | 
 | ||||||
|  | - Add binary morphology addon | ||||||
|  |   [dov, wiredfool] | ||||||
|  | 
 | ||||||
|  | - Decompression bomb protection | ||||||
|  |   [hugovk] | ||||||
|  | 
 | ||||||
|  | - Put images in a single directory | ||||||
|  |   [hugovk] | ||||||
|  | 
 | ||||||
|  | - Support OpenJpeg 2.1 | ||||||
|  |   [al45tair] | ||||||
|  | 
 | ||||||
|  | - Remove unistd.h #include for all platforms | ||||||
|  |   [wiredfool] | ||||||
|  | 
 | ||||||
| - Use unittest for tests | - Use unittest for tests | ||||||
|   [hugovk] |   [hugovk] | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										29
									
								
								PIL/Image.py
									
									
									
									
									
								
							
							
						
						|  | @ -31,11 +31,18 @@ from PIL import VERSION, PILLOW_VERSION, _plugins | ||||||
| import warnings | import warnings | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class DecompressionBombWarning(RuntimeWarning): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
| class _imaging_not_installed: | class _imaging_not_installed: | ||||||
|     # module placeholder |     # module placeholder | ||||||
|     def __getattr__(self, id): |     def __getattr__(self, id): | ||||||
|         raise ImportError("The _imaging C module is not installed") |         raise ImportError("The _imaging C module is not installed") | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | # Limit to around a quarter gigabyte for a 24 bit (3 bpp) image | ||||||
|  | MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 / 4 / 3) | ||||||
|  | 
 | ||||||
| try: | try: | ||||||
|     # give Tk a chance to set up the environment, in case we're |     # give Tk a chance to set up the environment, in case we're | ||||||
|     # using an _imaging module linked against libtcl/libtk (use |     # using an _imaging module linked against libtcl/libtk (use | ||||||
|  | @ -2172,6 +2179,20 @@ _fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") | ||||||
| _fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") | _fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def _decompression_bomb_check(size): | ||||||
|  |     if MAX_IMAGE_PIXELS is None: | ||||||
|  |         return | ||||||
|  | 
 | ||||||
|  |     pixels = size[0] * size[1] | ||||||
|  | 
 | ||||||
|  |     if pixels > MAX_IMAGE_PIXELS: | ||||||
|  |         warnings.warn( | ||||||
|  |             "Image size (%d pixels) exceeds limit of %d pixels, " | ||||||
|  |             "could be decompression bomb DOS attack." % | ||||||
|  |             (pixels, MAX_IMAGE_PIXELS), | ||||||
|  |             DecompressionBombWarning) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def open(fp, mode="r"): | def open(fp, mode="r"): | ||||||
|     """ |     """ | ||||||
|     Opens and identifies the given image file. |     Opens and identifies the given image file. | ||||||
|  | @ -2209,7 +2230,9 @@ def open(fp, mode="r"): | ||||||
|             factory, accept = OPEN[i] |             factory, accept = OPEN[i] | ||||||
|             if not accept or accept(prefix): |             if not accept or accept(prefix): | ||||||
|                 fp.seek(0) |                 fp.seek(0) | ||||||
|                 return factory(fp, filename) |                 im = factory(fp, filename) | ||||||
|  |                 _decompression_bomb_check(im.size) | ||||||
|  |                 return im | ||||||
|         except (SyntaxError, IndexError, TypeError): |         except (SyntaxError, IndexError, TypeError): | ||||||
|             # import traceback |             # import traceback | ||||||
|             # traceback.print_exc() |             # traceback.print_exc() | ||||||
|  | @ -2222,7 +2245,9 @@ def open(fp, mode="r"): | ||||||
|                 factory, accept = OPEN[i] |                 factory, accept = OPEN[i] | ||||||
|                 if not accept or accept(prefix): |                 if not accept or accept(prefix): | ||||||
|                     fp.seek(0) |                     fp.seek(0) | ||||||
|                     return factory(fp, filename) |                     im = factory(fp, filename) | ||||||
|  |                     _decompression_bomb_check(im.size) | ||||||
|  |                     return im | ||||||
|             except (SyntaxError, IndexError, TypeError): |             except (SyntaxError, IndexError, TypeError): | ||||||
|                 # import traceback |                 # import traceback | ||||||
|                 # traceback.print_exc() |                 # traceback.print_exc() | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| # | # | ||||||
| # For a background, see "Image Processing By Interpolation and | # For a background, see "Image Processing By Interpolation and | ||||||
| # Extrapolation", Paul Haeberli and Douglas Voorhies.  Available | # 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: | # History: | ||||||
| # 1996-03-23 fl  Created | # 1996-03-23 fl  Created | ||||||
|  |  | ||||||
|  | @ -396,7 +396,7 @@ w7IkEbzhVQAAAABJRU5ErkJggg== | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     # create font data chunk for embedding |     # create font data chunk for embedding | ||||||
|     import base64, os, sys |     import base64, os, sys | ||||||
|     font = "../Images/courB08" |     font = "../Tests/images/courB08" | ||||||
|     print("    f._load_pilfont_data(") |     print("    f._load_pilfont_data(") | ||||||
|     print("         # %s" % os.path.basename(font)) |     print("         # %s" % os.path.basename(font)) | ||||||
|     print("         BytesIO(base64.decodestring(b'''") |     print("         BytesIO(base64.decodestring(b'''") | ||||||
|  |  | ||||||
							
								
								
									
										236
									
								
								PIL/ImageMorph.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,236 @@ | ||||||
|  | # A binary morphology add-on for the Python Imaging Library | ||||||
|  | # | ||||||
|  | # History: | ||||||
|  | #   2014-06-04 Initial version. | ||||||
|  | # | ||||||
|  | # Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com> | ||||||
|  | 
 | ||||||
|  | from PIL import Image | ||||||
|  | from PIL import _imagingmorph | ||||||
|  | import re | ||||||
|  | 
 | ||||||
|  | LUT_SIZE = 1<<9 | ||||||
|  | class LutBuilder: | ||||||
|  |     """A class for building MorphLut's from a descriptive language | ||||||
|  | 
 | ||||||
|  |       The input patterns is a list of a strings sequences like these: | ||||||
|  | 
 | ||||||
|  |       4:(... | ||||||
|  |          .1. | ||||||
|  |          111)->1 | ||||||
|  | 
 | ||||||
|  |       (whitespaces including linebreaks are ignored). The option 4 | ||||||
|  |       descibes a series of symmetry operations (in this case a | ||||||
|  |       4-rotation), the pattern is decribed by: | ||||||
|  | 
 | ||||||
|  |          . or X - Ignore | ||||||
|  |          1 - Pixel is on | ||||||
|  |          0 - Pixel is off | ||||||
|  | 
 | ||||||
|  |       The result of the operation is described after "->" string. | ||||||
|  | 
 | ||||||
|  |       The default is to return the current pixel value, which is | ||||||
|  |       returned if no other match is found. | ||||||
|  | 
 | ||||||
|  |       Operations: | ||||||
|  |          4 - 4 way rotation | ||||||
|  |          N - Negate | ||||||
|  |          1 - Dummy op for no other operation (an op must always be given) | ||||||
|  |          M - Mirroring | ||||||
|  | 
 | ||||||
|  |       Example: | ||||||
|  | 
 | ||||||
|  |       lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) | ||||||
|  |       lut = lb.build_lut() | ||||||
|  |           | ||||||
|  |     """ | ||||||
|  |     def __init__(self,patterns = None,op_name=None): | ||||||
|  |         if patterns is not None: | ||||||
|  |             self.patterns = patterns | ||||||
|  |         else: | ||||||
|  |             self.patterns = [] | ||||||
|  |         self.lut = None | ||||||
|  |         if op_name is not None: | ||||||
|  |             known_patterns = { | ||||||
|  |                 'corner' : ['1:(... ... ...)->0', | ||||||
|  |                             '4:(00. 01. ...)->1'], | ||||||
|  |                 'dilation4' : ['4:(... .0. .1.)->1'], | ||||||
|  |                 'dilation8' : ['4:(... .0. .1.)->1', | ||||||
|  |                                '4:(... .0. ..1)->1'], | ||||||
|  |                 'erosion4' : ['4:(... .1. .0.)->0'], | ||||||
|  |                 'erosion8' : ['4:(... .1. .0.)->0', | ||||||
|  |                               '4:(... .1. ..0)->0'], | ||||||
|  |                 'edge' : ['1:(... ... ...)->0', | ||||||
|  |                           '4:(.0. .1. ...)->1', | ||||||
|  |                           '4:(01. .1. ...)->1'] | ||||||
|  |             } | ||||||
|  |             if not op_name in known_patterns: | ||||||
|  |                 raise Exception('Unknown pattern '+op_name+'!') | ||||||
|  | 
 | ||||||
|  |             self.patterns = known_patterns[op_name] | ||||||
|  | 
 | ||||||
|  |     def add_patterns(self, patterns): | ||||||
|  |         self.patterns += patterns | ||||||
|  | 
 | ||||||
|  |     def build_default_lut(self): | ||||||
|  |         symbols = [0, 1] | ||||||
|  |         m = 1 << 4  # pos of current pixel | ||||||
|  |         self.lut = bytearray([symbols[(i & m)>0] for i in range(LUT_SIZE)]) | ||||||
|  | 
 | ||||||
|  |     def get_lut(self): | ||||||
|  |         return self.lut | ||||||
|  | 
 | ||||||
|  |     def _string_permute(self, pattern, permutation): | ||||||
|  |         """string_permute takes a pattern and a permutation and returns the | ||||||
|  |         string permuted accordinging to the permutation list. | ||||||
|  |         """ | ||||||
|  |         assert(len(permutation)==9) | ||||||
|  |         return ''.join([pattern[p] for p in permutation]) | ||||||
|  | 
 | ||||||
|  |     def _pattern_permute(self, basic_pattern, options, basic_result): | ||||||
|  |         """pattern_permute takes a basic pattern and its result and clones | ||||||
|  |         the mattern according to the modifications described in the $options | ||||||
|  |         parameter. It returns a list of all cloned patterns.""" | ||||||
|  |         patterns = [(basic_pattern, basic_result)] | ||||||
|  | 
 | ||||||
|  |         # rotations | ||||||
|  |         if '4' in options: | ||||||
|  |             res = patterns[-1][1] | ||||||
|  |             for i in range(4): | ||||||
|  |                 patterns.append( | ||||||
|  |                     (self._string_permute(patterns[-1][0], | ||||||
|  |                                     [6,3,0, | ||||||
|  |                                      7,4,1, | ||||||
|  |                                      8,5,2]), res)) | ||||||
|  |         # mirror | ||||||
|  |         if 'M' in options: | ||||||
|  |             n = len(patterns) | ||||||
|  |             for pattern,res in patterns[0:n]: | ||||||
|  |                 patterns.append( | ||||||
|  |                     (self._string_permute(pattern, [2,1,0, | ||||||
|  | 					      5,4,3, | ||||||
|  | 					      8,7,6]), res)) | ||||||
|  | 
 | ||||||
|  |         # negate | ||||||
|  |         if 'N' in options: | ||||||
|  |             n = len(patterns) | ||||||
|  |             for pattern,res in patterns[0:n]: | ||||||
|  |                 # Swap 0 and 1 | ||||||
|  |                 pattern = (pattern | ||||||
|  |                            .replace('0','Z') | ||||||
|  |                            .replace('1','0') | ||||||
|  |                            .replace('Z','1')) | ||||||
|  |                 res = '%d'%(1-int(res)) | ||||||
|  |                 patterns.append((pattern, res)) | ||||||
|  | 
 | ||||||
|  |         return patterns | ||||||
|  | 
 | ||||||
|  |     def build_lut(self): | ||||||
|  |         """Compile all patterns into a morphology lut. | ||||||
|  | 
 | ||||||
|  |         TBD :Build based on (file) morphlut:modify_lut | ||||||
|  |         """ | ||||||
|  |         self.build_default_lut() | ||||||
|  |         patterns = [] | ||||||
|  | 
 | ||||||
|  |         # Parse and create symmetries of the patterns strings | ||||||
|  |         for p in self.patterns: | ||||||
|  |             m = re.search(r'(\w*):?\s*\((.+?)\)\s*->\s*(\d)', p.replace('\n','')) | ||||||
|  |             if not m: | ||||||
|  |                 raise Exception('Syntax error in pattern "'+p+'"') | ||||||
|  |             options = m.group(1) | ||||||
|  |             pattern = m.group(2) | ||||||
|  |             result = int(m.group(3)) | ||||||
|  | 
 | ||||||
|  |             # Get rid of spaces | ||||||
|  |             pattern= pattern.replace(' ','').replace('\n','') | ||||||
|  | 
 | ||||||
|  |             patterns += self._pattern_permute(pattern, options, result) | ||||||
|  | 
 | ||||||
|  | #        # Debugging | ||||||
|  | #        for p,r in patterns: | ||||||
|  | #            print p,r | ||||||
|  | #        print '--' | ||||||
|  | 
 | ||||||
|  |         # compile the patterns into regular expressions for speed | ||||||
|  |         for i in range(len(patterns)): | ||||||
|  |             p = patterns[i][0].replace('.','X').replace('X','[01]') | ||||||
|  |             p = re.compile(p) | ||||||
|  |             patterns[i] = (p, patterns[i][1]) | ||||||
|  | 
 | ||||||
|  |         # Step through table and find patterns that match. | ||||||
|  |         # Note that all the patterns are searched. The last one | ||||||
|  |         # caught overrides | ||||||
|  |         for i in range(LUT_SIZE): | ||||||
|  |             # Build the bit pattern | ||||||
|  |             bitpattern = bin(i)[2:] | ||||||
|  |             bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1] | ||||||
|  | 
 | ||||||
|  |             for p,r in patterns: | ||||||
|  |                 if p.match(bitpattern): | ||||||
|  |                     self.lut[i] = [0, 1][r] | ||||||
|  | 
 | ||||||
|  |         return self.lut | ||||||
|  |          | ||||||
|  | class MorphOp: | ||||||
|  |     """A class for binary morphological operators""" | ||||||
|  | 
 | ||||||
|  |     def __init__(self, | ||||||
|  |                  lut=None, | ||||||
|  |                  op_name = None, | ||||||
|  |                  patterns = None): | ||||||
|  |         """Create a binary morphological operator""" | ||||||
|  |         self.lut = lut | ||||||
|  |         if op_name is not None: | ||||||
|  |             self.lut = LutBuilder(op_name = op_name).build_lut() | ||||||
|  |         elif patterns is not None: | ||||||
|  |             self.lut = LutBuilder(patterns = patterns).build_lut() | ||||||
|  | 
 | ||||||
|  |     def apply(self, image): | ||||||
|  |         """Run a single morphological operation on an image | ||||||
|  | 
 | ||||||
|  |         Returns a tuple of the number of changed pixels and the | ||||||
|  |         morphed image""" | ||||||
|  |         if self.lut is None: | ||||||
|  |             raise Exception('No operator loaded') | ||||||
|  |          | ||||||
|  |         outimage = Image.new(image.mode, image.size, None) | ||||||
|  |         count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id) | ||||||
|  |         return count, outimage | ||||||
|  |      | ||||||
|  |     def match(self, image): | ||||||
|  |         """Get a list of coordinates matching the morphological operation on an image | ||||||
|  | 
 | ||||||
|  |         Returns a list of tuples of (x,y) coordinates of all matching pixels.""" | ||||||
|  |         if self.lut is None: | ||||||
|  |             raise Exception('No operator loaded') | ||||||
|  |          | ||||||
|  |         return  _imagingmorph.match(bytes(self.lut), image.im.id) | ||||||
|  | 
 | ||||||
|  |     def get_on_pixels(self, image): | ||||||
|  |         """Get a list of all turned on pixels in a binary image | ||||||
|  |         Returns a list of tuples of (x,y) coordinates of all matching pixels.""" | ||||||
|  |          | ||||||
|  |         return  _imagingmorph.get_on_pixels(image.im.id) | ||||||
|  | 
 | ||||||
|  |     def load_lut(self, filename): | ||||||
|  |         """Load an operator from an mrl file""" | ||||||
|  |         with open(filename,'rb') as f: | ||||||
|  |             self.lut = bytearray(f.read()) | ||||||
|  |              | ||||||
|  |         if len(self.lut)!= 8192: | ||||||
|  |             self.lut = None | ||||||
|  |             raise Exception('Wrong size operator file!') | ||||||
|  | 
 | ||||||
|  |     def save_lut(self, filename): | ||||||
|  |         """Load an operator save mrl file""" | ||||||
|  |         if self.lut is None: | ||||||
|  |             raise Exception('No operator loaded') | ||||||
|  |         with open(filename,'wb') as f: | ||||||
|  |             f.write(self.lut) | ||||||
|  | 
 | ||||||
|  |     def set_lut(self, lut): | ||||||
|  |         """Set the lut from an external source""" | ||||||
|  |         self.lut = lut | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  | @ -158,15 +158,25 @@ class Jpeg2KImageFile(ImageFile.ImageFile): | ||||||
|         self.layers = 0 |         self.layers = 0 | ||||||
| 
 | 
 | ||||||
|         fd = -1 |         fd = -1 | ||||||
|  |         length = -1 | ||||||
| 
 | 
 | ||||||
|         if hasattr(self.fp, "fileno"): |         if hasattr(self.fp, "fileno"): | ||||||
|             try: |             try: | ||||||
|                 fd = self.fp.fileno() |                 fd = self.fp.fileno() | ||||||
|  |                 length = os.fstat(fd).st_size | ||||||
|             except: |             except: | ||||||
|                 fd = -1 |                 fd = -1 | ||||||
|  |         elif hasattr(self.fp, "seek"): | ||||||
|  |             try: | ||||||
|  |                 pos = f.tell() | ||||||
|  |                 f.seek(0, 2) | ||||||
|  |                 length = f.tell() | ||||||
|  |                 f.seek(pos, 0) | ||||||
|  |             except: | ||||||
|  |                 length = -1 | ||||||
|          |          | ||||||
|         self.tile = [('jpeg2k', (0, 0) + self.size, 0, |         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): |     def load(self): | ||||||
|         if self.reduce: |         if self.reduce: | ||||||
|  | @ -178,7 +188,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile): | ||||||
|         if self.tile: |         if self.tile: | ||||||
|             # Update the reduce and layers settings |             # Update the reduce and layers settings | ||||||
|             t = self.tile[0] |             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)] |             self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] | ||||||
| 
 | 
 | ||||||
|         ImageFile.ImageFile.load(self) |         ImageFile.ImageFile.load(self) | ||||||
|  |  | ||||||
|  | @ -14,11 +14,11 @@ | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
| # NOTE: This format cannot be automatically recognized, so the reader | # 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. | # the WalImageFile.open() function instead. | ||||||
| 
 | 
 | ||||||
| # This reader is based on the specification available from: | # 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. | # and has been tested with a few sample files found using google. | ||||||
| 
 | 
 | ||||||
| from __future__ import print_function | from __future__ import print_function | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| 
 |  | ||||||
| Python SANE module V1.1 (30 Sep. 2004) | Python SANE module V1.1 (30 Sep. 2004) | ||||||
|  | ================================================================================ | ||||||
| 
 | 
 | ||||||
| The SANE module provides an interface to the SANE scanner and frame | The SANE module provides an interface to the SANE scanner and frame | ||||||
| grabber interface for Linux.  This module was contributed by Andrew | 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. | 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 | 	python setup.py build | ||||||
| 
 | 
 | ||||||
| In order to install the module type: | In order to install the module type:: | ||||||
| 
 | 
 | ||||||
| 	python setup.py install | 	python setup.py install | ||||||
| 
 | 
 | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| ------- |  | ||||||
| Scripts | Scripts | ||||||
| ------- | ======= | ||||||
| 
 | 
 | ||||||
| This directory contains a number of more or less trivial utilities | This directory contains a number of more or less trivial utilities | ||||||
| and demo programs. | and demo programs. | ||||||
|  | @ -9,50 +8,50 @@ Comments and contributions are welcome. | ||||||
| 
 | 
 | ||||||
| </F> | </F> | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| pildriver.py (by Eric S. Raymond) | pildriver.py (by Eric S. Raymond) | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| A class implementing an image-processing calculator for scripts. | A class implementing an image-processing calculator for scripts. | ||||||
| Parses lists of commnds (or, called interactively, command-line | Parses lists of commnds (or, called interactively, command-line | ||||||
| arguments) into image loads, transformations, and saves. | arguments) into image loads, transformations, and saves. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| viewer.py | viewer.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| A simple image viewer.  Can display all file formats handled by | A simple image viewer.  Can display all file formats handled by | ||||||
| PIL.  Transparent images are properly handled. | PIL.  Transparent images are properly handled. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| thresholder.py | thresholder.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| A simple utility that demonstrates how a transparent 1-bit overlay | A simple utility that demonstrates how a transparent 1-bit overlay | ||||||
| can be used to show the current thresholding of an 8-bit image. | can be used to show the current thresholding of an 8-bit image. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| enhancer.py | enhancer.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| Illustrates the ImageEnhance module.  Drag the sliders to modify the | Illustrates the ImageEnhance module.  Drag the sliders to modify the | ||||||
| images.  This might be very slow on some platforms, depending on the | images.  This might be very slow on some platforms, depending on the | ||||||
| Tk version. | Tk version. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| painter.py | painter.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| Illustrates how a painting program could be based on PIL and Tk. | 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 | Press the left mouse button and drag over the image to remove the | ||||||
| colour.  Some clever tricks have been used to get decent performance | colour.  Some clever tricks have been used to get decent performance | ||||||
| when updating the screen; see the sources for details. | when updating the screen; see the sources for details. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| player.py | player.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| A simple image sequence player.  You can use either a sequence format | 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 | 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 | interpreted as frames in a sequence.  All frames must have the same | ||||||
| size. | size. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| gifmaker.py | gifmaker.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| Convert a sequence file to a GIF animation. | 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 | uncompressed GIF files only, so the resulting animations are rather | ||||||
| large compared with these created by other tools. | large compared with these created by other tools. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| explode.py | explode.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| Split a sequence file into individual frames. | Split a sequence file into individual frames. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| image2py.py | image2py.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| Convert an image to a Python module containing an IMAGE variable. | Convert an image to a Python module containing an IMAGE variable. | ||||||
| Note that the module using the module must include JPEG and ZIP | Note that the module using the module must include JPEG and ZIP | ||||||
| decoders, unless the -u option is used. | decoders, unless the -u option is used. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------- |  | ||||||
| olesummary.py | olesummary.py | ||||||
|  | -------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| Uses the OleFileIO module to dump the summary information from an OLE | Uses the OleFileIO module to dump the summary information from an OLE | ||||||
| structured storage file.  This works with most OLE files, including | structured storage file.  This works with most OLE files, including | ||||||
|  | @ -233,7 +233,7 @@ def lena(mode="RGB", cache={}): | ||||||
|     # im = cache.get(mode) |     # im = cache.get(mode) | ||||||
|     if im is None: |     if im is None: | ||||||
|         if mode == "RGB": |         if mode == "RGB": | ||||||
|             im = Image.open("Images/lena.ppm") |             im = Image.open("Tests/images/lena.ppm") | ||||||
|         elif mode == "F": |         elif mode == "F": | ||||||
|             im = lena("L").convert(mode) |             im = lena("L").convert(mode) | ||||||
|         elif mode[:4] == "I;16": |         elif mode[:4] == "I;16": | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								Tests/images/corner.lut
									
									
									
									
									
										Normal file
									
								
							
							
						
						| Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/dilation4.lut
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/dilation8.lut
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/edge.lut
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/erosion4.lut
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Tests/images/erosion8.lut
									
									
									
									
									
										Normal file
									
								
							
							
						
						| Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB | 
| Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB | 
| Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB | 
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB | 
| Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB | 
| Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/morph_a.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 83 B | 
| Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB | 
| Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB | 
| Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB | 
							
								
								
									
										45
									
								
								Tests/test_decompression_bomb.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,45 @@ | ||||||
|  | from helper import unittest, PillowTestCase, tearDownModule | ||||||
|  | 
 | ||||||
|  | from PIL import Image | ||||||
|  | 
 | ||||||
|  | test_file = "Tests/images/lena.ppm" | ||||||
|  | 
 | ||||||
|  | ORIGINAL_LIMIT = Image.MAX_IMAGE_PIXELS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TestDecompressionBomb(PillowTestCase): | ||||||
|  | 
 | ||||||
|  |     def tearDown(self): | ||||||
|  |         Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT | ||||||
|  | 
 | ||||||
|  |     def test_no_warning_small_file(self): | ||||||
|  |         # Implicit assert: no warning. | ||||||
|  |         # A warning would cause a failure. | ||||||
|  |         Image.open(test_file) | ||||||
|  | 
 | ||||||
|  |     def test_no_warning_no_limit(self): | ||||||
|  |         # Arrange | ||||||
|  |         # Turn limit off | ||||||
|  |         Image.MAX_IMAGE_PIXELS = None | ||||||
|  |         self.assertEqual(Image.MAX_IMAGE_PIXELS, None) | ||||||
|  | 
 | ||||||
|  |         # Act / Assert | ||||||
|  |         # Implicit assert: no warning. | ||||||
|  |         # A warning would cause a failure. | ||||||
|  |         Image.open(test_file) | ||||||
|  | 
 | ||||||
|  |     def test_warning(self): | ||||||
|  |         # Arrange | ||||||
|  |         # Set limit to a low, easily testable value | ||||||
|  |         Image.MAX_IMAGE_PIXELS = 10 | ||||||
|  |         self.assertEqual(Image.MAX_IMAGE_PIXELS, 10) | ||||||
|  | 
 | ||||||
|  |         # Act / Assert | ||||||
|  |         self.assert_warning( | ||||||
|  |             Image.DecompressionBombWarning, | ||||||
|  |             lambda: Image.open(test_file)) | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     unittest.main() | ||||||
|  | 
 | ||||||
|  | # End of file | ||||||
|  | @ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule | ||||||
| from PIL import Image | from PIL import Image | ||||||
| 
 | 
 | ||||||
| # sample ppm stream | # sample ppm stream | ||||||
| file = "Images/lena.fli" | file = "Tests/images/lena.fli" | ||||||
| data = open(file, "rb").read() | data = open(file, "rb").read() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ from PIL import Image | ||||||
| codecs = dir(Image.core) | codecs = dir(Image.core) | ||||||
| 
 | 
 | ||||||
| # sample gif stream | # sample gif stream | ||||||
| file = "Images/lena.gif" | file = "Tests/images/lena.gif" | ||||||
| with open(file, "rb") as f: | with open(file, "rb") as f: | ||||||
|     data = f.read() |     data = f.read() | ||||||
| 
 | 
 | ||||||
|  | @ -45,7 +45,7 @@ class TestFileGif(PillowTestCase): | ||||||
|     def test_roundtrip2(self): |     def test_roundtrip2(self): | ||||||
|         # see https://github.com/python-pillow/Pillow/issues/403 |         # see https://github.com/python-pillow/Pillow/issues/403 | ||||||
|         out = self.tempfile('temp.gif') |         out = self.tempfile('temp.gif') | ||||||
|         im = Image.open('Images/lena.gif') |         im = Image.open('Tests/images/lena.gif') | ||||||
|         im2 = im.copy() |         im2 = im.copy() | ||||||
|         im2.save(out) |         im2.save(out) | ||||||
|         reread = Image.open(out) |         reread = Image.open(out) | ||||||
|  | @ -55,7 +55,7 @@ class TestFileGif(PillowTestCase): | ||||||
|     def test_palette_handling(self): |     def test_palette_handling(self): | ||||||
|         # see https://github.com/python-pillow/Pillow/issues/513 |         # see https://github.com/python-pillow/Pillow/issues/513 | ||||||
| 
 | 
 | ||||||
|         im = Image.open('Images/lena.gif') |         im = Image.open('Tests/images/lena.gif') | ||||||
|         im = im.convert('RGB') |         im = im.convert('RGB') | ||||||
| 
 | 
 | ||||||
|         im = im.resize((100, 100), Image.ANTIALIAS) |         im = im.resize((100, 100), Image.ANTIALIAS) | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule | ||||||
| from PIL import Image | from PIL import Image | ||||||
| 
 | 
 | ||||||
| # sample icon file | # sample icon file | ||||||
| file = "Images/pillow.icns" | file = "Tests/images/pillow.icns" | ||||||
| data = open(file, "rb").read() | data = open(file, "rb").read() | ||||||
| 
 | 
 | ||||||
| enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') | enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule | ||||||
| from PIL import Image | from PIL import Image | ||||||
| 
 | 
 | ||||||
| # sample ppm stream | # sample ppm stream | ||||||
| file = "Images/lena.ico" | file = "Tests/images/lena.ico" | ||||||
| data = open(file, "rb").read() | data = open(file, "rb").read() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ codecs = dir(Image.core) | ||||||
| 
 | 
 | ||||||
| # sample png stream | # sample png stream | ||||||
| 
 | 
 | ||||||
| file = "Images/lena.png" | file = "Tests/images/lena.png" | ||||||
| data = open(file, "rb").read() | data = open(file, "rb").read() | ||||||
| 
 | 
 | ||||||
| # stuff to create inline PNG images | # stuff to create inline PNG images | ||||||
|  | @ -204,10 +204,10 @@ class TestFilePng(PillowTestCase): | ||||||
|     def test_load_verify(self): |     def test_load_verify(self): | ||||||
|         # Check open/load/verify exception (@PIL150) |         # Check open/load/verify exception (@PIL150) | ||||||
| 
 | 
 | ||||||
|         im = Image.open("Images/lena.png") |         im = Image.open("Tests/images/lena.png") | ||||||
|         im.verify() |         im.verify() | ||||||
| 
 | 
 | ||||||
|         im = Image.open("Images/lena.png") |         im = Image.open("Tests/images/lena.png") | ||||||
|         im.load() |         im.load() | ||||||
|         self.assertRaises(RuntimeError, lambda: im.verify()) |         self.assertRaises(RuntimeError, lambda: im.verify()) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule | ||||||
| from PIL import Image | from PIL import Image | ||||||
| 
 | 
 | ||||||
| # sample ppm stream | # sample ppm stream | ||||||
| file = "Images/lena.ppm" | file = "Tests/images/lena.ppm" | ||||||
| data = open(file, "rb").read() | data = open(file, "rb").read() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule | ||||||
| from PIL import Image | from PIL import Image | ||||||
| 
 | 
 | ||||||
| # sample ppm stream | # sample ppm stream | ||||||
| file = "Images/lena.psd" | file = "Tests/images/lena.psd" | ||||||
| data = open(file, "rb").read() | data = open(file, "rb").read() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ from PIL import Image, TarIO | ||||||
| codecs = dir(Image.core) | codecs = dir(Image.core) | ||||||
| 
 | 
 | ||||||
| # sample ppm stream | # sample ppm stream | ||||||
| tarfile = "Images/lena.tar" | tarfile = "Tests/images/lena.tar" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestFileTar(PillowTestCase): | class TestFileTar(PillowTestCase): | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ class TestFileWebp(PillowTestCase): | ||||||
| 
 | 
 | ||||||
|     def test_read_rgb(self): |     def test_read_rgb(self): | ||||||
| 
 | 
 | ||||||
|         file_path = "Images/lena.webp" |         file_path = "Tests/images/lena.webp" | ||||||
|         image = Image.open(file_path) |         image = Image.open(file_path) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(image.mode, "RGB") |         self.assertEqual(image.mode, "RGB") | ||||||
|  | @ -33,7 +33,7 @@ class TestFileWebp(PillowTestCase): | ||||||
|         image.getdata() |         image.getdata() | ||||||
| 
 | 
 | ||||||
|         # generated with: |         # generated with: | ||||||
|         # dwebp -ppm ../../Images/lena.webp -o lena_webp_bits.ppm |         # dwebp -ppm ../../Tests/images/lena.webp -o lena_webp_bits.ppm | ||||||
|         target = Image.open('Tests/images/lena_webp_bits.ppm') |         target = Image.open('Tests/images/lena_webp_bits.ppm') | ||||||
|         self.assert_image_similar(image, target, 20.0) |         self.assert_image_similar(image, target, 20.0) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ class TestFileWebpAlpha(PillowTestCase): | ||||||
| 
 | 
 | ||||||
|     def test_read_rgba(self): |     def test_read_rgba(self): | ||||||
|         # Generated with `cwebp transparent.png -o transparent.webp` |         # Generated with `cwebp transparent.png -o transparent.webp` | ||||||
|         file_path = "Images/transparent.webp" |         file_path = "Tests/images/transparent.webp" | ||||||
|         image = Image.open(file_path) |         image = Image.open(file_path) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(image.mode, "RGBA") |         self.assertEqual(image.mode, "RGBA") | ||||||
|  | @ -33,7 +33,7 @@ class TestFileWebpAlpha(PillowTestCase): | ||||||
| 
 | 
 | ||||||
|         image.tobytes() |         image.tobytes() | ||||||
| 
 | 
 | ||||||
|         target = Image.open('Images/transparent.png') |         target = Image.open('Tests/images/transparent.png') | ||||||
|         self.assert_image_similar(image, target, 20.0) |         self.assert_image_similar(image, target, 20.0) | ||||||
| 
 | 
 | ||||||
|     def test_write_lossless_rgb(self): |     def test_write_lossless_rgb(self): | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ class TestFileWebpMetadata(PillowTestCase): | ||||||
| 
 | 
 | ||||||
|     def test_read_exif_metadata(self): |     def test_read_exif_metadata(self): | ||||||
| 
 | 
 | ||||||
|         file_path = "Images/flower.webp" |         file_path = "Tests/images/flower.webp" | ||||||
|         image = Image.open(file_path) |         image = Image.open(file_path) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(image.format, "WEBP") |         self.assertEqual(image.format, "WEBP") | ||||||
|  | @ -54,7 +54,7 @@ class TestFileWebpMetadata(PillowTestCase): | ||||||
| 
 | 
 | ||||||
|     def test_read_icc_profile(self): |     def test_read_icc_profile(self): | ||||||
| 
 | 
 | ||||||
|         file_path = "Images/flower2.webp" |         file_path = "Tests/images/flower2.webp" | ||||||
|         image = Image.open(file_path) |         image = Image.open(file_path) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(image.format, "WEBP") |         self.assertEqual(image.format, "WEBP") | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule | ||||||
| from PIL import Image | from PIL import Image | ||||||
| 
 | 
 | ||||||
| # sample ppm stream | # sample ppm stream | ||||||
| file = "Images/lena.xpm" | file = "Tests/images/lena.xpm" | ||||||
| data = open(file, "rb").read() | data = open(file, "rb").read() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ from helper import unittest, PillowTestCase, tearDownModule | ||||||
| 
 | 
 | ||||||
| from PIL import FontFile, BdfFontFile | from PIL import FontFile, BdfFontFile | ||||||
| 
 | 
 | ||||||
| filename = "Images/courB08.bdf" | filename = "Tests/images/courB08.bdf" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestFontBdf(PillowTestCase): | class TestFontBdf(PillowTestCase): | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ class TestImageConvert(PillowTestCase): | ||||||
|         self.assertEqual(orig, converted) |         self.assertEqual(orig, converted) | ||||||
| 
 | 
 | ||||||
|     def test_8bit(self): |     def test_8bit(self): | ||||||
|         im = Image.open('Images/lena.jpg') |         im = Image.open('Tests/images/lena.jpg') | ||||||
|         self._test_float_conversion(im.convert('L')) |         self._test_float_conversion(im.convert('L')) | ||||||
| 
 | 
 | ||||||
|     def test_16bit(self): |     def test_16bit(self): | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule, fromstring, tostrin | ||||||
| from PIL import Image | from PIL import Image | ||||||
| 
 | 
 | ||||||
| codecs = dir(Image.core) | codecs = dir(Image.core) | ||||||
| filename = "Images/lena.jpg" | filename = "Tests/images/lena.jpg" | ||||||
| data = tostring(Image.open(filename).resize((512, 512)), "JPEG") | data = tostring(Image.open(filename).resize((512, 512)), "JPEG") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,14 +16,14 @@ class TestImageLoad(PillowTestCase): | ||||||
|         self.assertEqual(pix[0, 0], (223, 162, 133)) |         self.assertEqual(pix[0, 0], (223, 162, 133)) | ||||||
| 
 | 
 | ||||||
|     def test_close(self): |     def test_close(self): | ||||||
|         im = Image.open("Images/lena.gif") |         im = Image.open("Tests/images/lena.gif") | ||||||
|         im.close() |         im.close() | ||||||
|         self.assertRaises(ValueError, lambda: im.load()) |         self.assertRaises(ValueError, lambda: im.load()) | ||||||
|         self.assertRaises(ValueError, lambda: im.getpixel((0, 0))) |         self.assertRaises(ValueError, lambda: im.getpixel((0, 0))) | ||||||
| 
 | 
 | ||||||
|     def test_contextmanager(self): |     def test_contextmanager(self): | ||||||
|         fn = None |         fn = None | ||||||
|         with Image.open("Images/lena.gif") as im: |         with Image.open("Tests/images/lena.gif") as im: | ||||||
|             fn = im.fp.fileno() |             fn = im.fp.fileno() | ||||||
|             os.fstat(fn) |             os.fstat(fn) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,6 +18,8 @@ class TestImageCms(PillowTestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         try: |         try: | ||||||
|             from PIL import ImageCms |             from PIL import ImageCms | ||||||
|  |             # need to hit getattr to trigger the delayed import error | ||||||
|  |             ImageCms.core.profile_open | ||||||
|         except ImportError as v: |         except ImportError as v: | ||||||
|             self.skipTest(v) |             self.skipTest(v) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,21 +7,21 @@ from PIL import ImageDraw | ||||||
| import sys | import sys | ||||||
| 
 | 
 | ||||||
| # Image size | # Image size | ||||||
| w, h = 100, 100 | W, H = 100, 100 | ||||||
| 
 | 
 | ||||||
| # Bounding box points | # Bounding box points | ||||||
| x0 = int(w / 4) | X0 = int(W / 4) | ||||||
| x1 = int(x0 * 3) | X1 = int(X0 * 3) | ||||||
| y0 = int(h / 4) | Y0 = int(H / 4) | ||||||
| y1 = int(x0 * 3) | Y1 = int(X0 * 3) | ||||||
| 
 | 
 | ||||||
| # Two kinds of bounding box | # Two kinds of bounding box | ||||||
| bbox1 = [(x0, y0), (x1, y1)] | BBOX1 = [(X0, Y0), (X1, Y1)] | ||||||
| bbox2 = [x0, y0, x1, y1] | BBOX2 = [X0, Y0, X1, Y1] | ||||||
| 
 | 
 | ||||||
| # Two kinds of coordinate sequences | # Two kinds of coordinate sequences | ||||||
| points1 = [(10, 10), (20, 40), (30, 30)] | POINTS1 = [(10, 10), (20, 40), (30, 30)] | ||||||
| points2 = [10, 10, 20, 40, 30, 30] | POINTS2 = [10, 10, 20, 40, 30, 30] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestImageDraw(PillowTestCase): | class TestImageDraw(PillowTestCase): | ||||||
|  | @ -47,7 +47,7 @@ class TestImageDraw(PillowTestCase): | ||||||
| 
 | 
 | ||||||
|     def helper_arc(self, bbox): |     def helper_arc(self, bbox): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|  | @ -60,15 +60,15 @@ class TestImageDraw(PillowTestCase): | ||||||
|             im, Image.open("Tests/images/imagedraw_arc.png")) |             im, Image.open("Tests/images/imagedraw_arc.png")) | ||||||
| 
 | 
 | ||||||
|     def test_arc1(self): |     def test_arc1(self): | ||||||
|         self.helper_arc(bbox1) |         self.helper_arc(BBOX1) | ||||||
| 
 | 
 | ||||||
|     def test_arc2(self): |     def test_arc2(self): | ||||||
|         self.helper_arc(bbox2) |         self.helper_arc(BBOX2) | ||||||
| 
 | 
 | ||||||
|     def test_bitmap(self): |     def test_bitmap(self): | ||||||
|         # Arrange |         # Arrange | ||||||
|         small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) |         small = Image.open("Tests/images/pil123rgba.png").resize((50, 50)) | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|  | @ -81,7 +81,7 @@ class TestImageDraw(PillowTestCase): | ||||||
| 
 | 
 | ||||||
|     def helper_chord(self, bbox): |     def helper_chord(self, bbox): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|  | @ -93,14 +93,14 @@ class TestImageDraw(PillowTestCase): | ||||||
|             im, Image.open("Tests/images/imagedraw_chord.png")) |             im, Image.open("Tests/images/imagedraw_chord.png")) | ||||||
| 
 | 
 | ||||||
|     def test_chord1(self): |     def test_chord1(self): | ||||||
|         self.helper_chord(bbox1) |         self.helper_chord(BBOX1) | ||||||
| 
 | 
 | ||||||
|     def test_chord2(self): |     def test_chord2(self): | ||||||
|         self.helper_chord(bbox2) |         self.helper_chord(BBOX2) | ||||||
| 
 | 
 | ||||||
|     def helper_ellipse(self, bbox): |     def helper_ellipse(self, bbox): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|  | @ -112,18 +112,18 @@ class TestImageDraw(PillowTestCase): | ||||||
|             im, Image.open("Tests/images/imagedraw_ellipse.png")) |             im, Image.open("Tests/images/imagedraw_ellipse.png")) | ||||||
| 
 | 
 | ||||||
|     def test_ellipse1(self): |     def test_ellipse1(self): | ||||||
|         self.helper_ellipse(bbox1) |         self.helper_ellipse(BBOX1) | ||||||
| 
 | 
 | ||||||
|     def test_ellipse2(self): |     def test_ellipse2(self): | ||||||
|         self.helper_ellipse(bbox2) |         self.helper_ellipse(BBOX2) | ||||||
| 
 | 
 | ||||||
|     def helper_line(self, points): |     def helper_line(self, points): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|         draw.line(points1, fill="yellow", width=2) |         draw.line(points, fill="yellow", width=2) | ||||||
|         del draw |         del draw | ||||||
| 
 | 
 | ||||||
|         # Assert |         # Assert | ||||||
|  | @ -131,14 +131,14 @@ class TestImageDraw(PillowTestCase): | ||||||
|             im, Image.open("Tests/images/imagedraw_line.png")) |             im, Image.open("Tests/images/imagedraw_line.png")) | ||||||
| 
 | 
 | ||||||
|     def test_line1(self): |     def test_line1(self): | ||||||
|         self.helper_line(points1) |         self.helper_line(POINTS1) | ||||||
| 
 | 
 | ||||||
|     def test_line2(self): |     def test_line2(self): | ||||||
|         self.helper_line(points2) |         self.helper_line(POINTS2) | ||||||
| 
 | 
 | ||||||
|     def helper_pieslice(self, bbox): |     def helper_pieslice(self, bbox): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|  | @ -150,18 +150,18 @@ class TestImageDraw(PillowTestCase): | ||||||
|             im, Image.open("Tests/images/imagedraw_pieslice.png")) |             im, Image.open("Tests/images/imagedraw_pieslice.png")) | ||||||
| 
 | 
 | ||||||
|     def test_pieslice1(self): |     def test_pieslice1(self): | ||||||
|         self.helper_pieslice(bbox1) |         self.helper_pieslice(BBOX1) | ||||||
| 
 | 
 | ||||||
|     def test_pieslice2(self): |     def test_pieslice2(self): | ||||||
|         self.helper_pieslice(bbox2) |         self.helper_pieslice(BBOX2) | ||||||
| 
 | 
 | ||||||
|     def helper_point(self, points): |     def helper_point(self, points): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|         draw.point(points1, fill="yellow") |         draw.point(points, fill="yellow") | ||||||
|         del draw |         del draw | ||||||
| 
 | 
 | ||||||
|         # Assert |         # Assert | ||||||
|  | @ -169,18 +169,18 @@ class TestImageDraw(PillowTestCase): | ||||||
|             im, Image.open("Tests/images/imagedraw_point.png")) |             im, Image.open("Tests/images/imagedraw_point.png")) | ||||||
| 
 | 
 | ||||||
|     def test_point1(self): |     def test_point1(self): | ||||||
|         self.helper_point(points1) |         self.helper_point(POINTS1) | ||||||
| 
 | 
 | ||||||
|     def test_point2(self): |     def test_point2(self): | ||||||
|         self.helper_point(points2) |         self.helper_point(POINTS2) | ||||||
| 
 | 
 | ||||||
|     def helper_polygon(self, points): |     def helper_polygon(self, points): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|         draw.polygon(points1, fill="red", outline="blue") |         draw.polygon(points, fill="red", outline="blue") | ||||||
|         del draw |         del draw | ||||||
| 
 | 
 | ||||||
|         # Assert |         # Assert | ||||||
|  | @ -188,14 +188,14 @@ class TestImageDraw(PillowTestCase): | ||||||
|             im, Image.open("Tests/images/imagedraw_polygon.png")) |             im, Image.open("Tests/images/imagedraw_polygon.png")) | ||||||
| 
 | 
 | ||||||
|     def test_polygon1(self): |     def test_polygon1(self): | ||||||
|         self.helper_polygon(points1) |         self.helper_polygon(POINTS1) | ||||||
| 
 | 
 | ||||||
|     def test_polygon2(self): |     def test_polygon2(self): | ||||||
|         self.helper_polygon(points2) |         self.helper_polygon(POINTS2) | ||||||
| 
 | 
 | ||||||
|     def helper_rectangle(self, bbox): |     def helper_rectangle(self, bbox): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|  | @ -207,17 +207,17 @@ class TestImageDraw(PillowTestCase): | ||||||
|             im, Image.open("Tests/images/imagedraw_rectangle.png")) |             im, Image.open("Tests/images/imagedraw_rectangle.png")) | ||||||
| 
 | 
 | ||||||
|     def test_rectangle1(self): |     def test_rectangle1(self): | ||||||
|         self.helper_rectangle(bbox1) |         self.helper_rectangle(BBOX1) | ||||||
| 
 | 
 | ||||||
|     def test_rectangle2(self): |     def test_rectangle2(self): | ||||||
|         self.helper_rectangle(bbox2) |         self.helper_rectangle(BBOX2) | ||||||
| 
 | 
 | ||||||
|     def test_floodfill(self): |     def test_floodfill(self): | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
|         draw.rectangle(bbox2, outline="yellow", fill="green") |         draw.rectangle(BBOX2, outline="yellow", fill="green") | ||||||
|         centre_point = (int(w/2), int(h/2)) |         centre_point = (int(W/2), int(H/2)) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|         ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) |         ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red")) | ||||||
|  | @ -233,10 +233,10 @@ class TestImageDraw(PillowTestCase): | ||||||
|         # floodfill() is experimental |         # floodfill() is experimental | ||||||
| 
 | 
 | ||||||
|         # Arrange |         # Arrange | ||||||
|         im = Image.new("RGB", (w, h)) |         im = Image.new("RGB", (W, H)) | ||||||
|         draw = ImageDraw.Draw(im) |         draw = ImageDraw.Draw(im) | ||||||
|         draw.rectangle(bbox2, outline="yellow", fill="green") |         draw.rectangle(BBOX2, outline="yellow", fill="green") | ||||||
|         centre_point = (int(w/2), int(h/2)) |         centre_point = (int(W/2), int(H/2)) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|         ImageDraw.floodfill( |         ImageDraw.floodfill( | ||||||
|  |  | ||||||
							
								
								
									
										169
									
								
								Tests/test_imagemorph.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,169 @@ | ||||||
|  | # Test the ImageMorphology functionality | ||||||
|  | from helper import * | ||||||
|  | 
 | ||||||
|  | from PIL import Image | ||||||
|  | from PIL import ImageMorph | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class MorphTests(PillowTestCase): | ||||||
|  | 
 | ||||||
|  |     def setUp(self): | ||||||
|  |         self.A = self.string_to_img( | ||||||
|  |             """     | ||||||
|  |             ....... | ||||||
|  |             ....... | ||||||
|  |             ..111.. | ||||||
|  |             ..111.. | ||||||
|  |             ..111.. | ||||||
|  |             ....... | ||||||
|  |             ....... | ||||||
|  |             """ | ||||||
|  |             )     | ||||||
|  |      | ||||||
|  | 
 | ||||||
|  |     def img_to_string(self, im): | ||||||
|  |         """Turn a (small) binary image into a string representation""" | ||||||
|  |         chars = '.1' | ||||||
|  |         width, height = im.size | ||||||
|  |         return '\n'.join( | ||||||
|  |             [''.join([chars[im.getpixel((c,r))>0] for c in range(width)]) | ||||||
|  |              for r in range(height)]) | ||||||
|  | 
 | ||||||
|  |     def string_to_img(self, image_string): | ||||||
|  |         """Turn a string image representation into a binary image""" | ||||||
|  |         rows = [s for s in image_string.replace(' ','').split('\n') | ||||||
|  |                 if len(s)] | ||||||
|  |         height = len(rows) | ||||||
|  |         width = len(rows[0]) | ||||||
|  |         im = Image.new('L',(width,height)) | ||||||
|  |         for i in range(width): | ||||||
|  |             for j in range(height): | ||||||
|  |                 c = rows[j][i] | ||||||
|  |                 v = c in 'X1' | ||||||
|  |                 im.putpixel((i,j),v) | ||||||
|  | 
 | ||||||
|  |         return im | ||||||
|  | 
 | ||||||
|  |     def img_string_normalize(self, im): | ||||||
|  |         return self.img_to_string(self.string_to_img(im)) | ||||||
|  | 
 | ||||||
|  |     def assert_img_equal(self, A, B): | ||||||
|  |         self.assertEqual(self.img_to_string(A), self.img_to_string(B)) | ||||||
|  | 
 | ||||||
|  |     def assert_img_equal_img_string(self, A, Bstring): | ||||||
|  |         self.assertEqual(self.img_to_string(A), self.img_string_normalize(Bstring)) | ||||||
|  | 
 | ||||||
|  |     def test_str_to_img(self): | ||||||
|  |         im = Image.open('Tests/images/morph_a.png') | ||||||
|  |         self.assert_image_equal(self.A, im) | ||||||
|  | 
 | ||||||
|  |     def create_lut(self): | ||||||
|  |         for op in ('corner', 'dilation4', 'dilation8', 'erosion4', 'erosion8', 'edge'): | ||||||
|  |             lb = ImageMorph.LutBuilder(op_name=op) | ||||||
|  |             lut = lb.build_lut(self) | ||||||
|  |             with open('Tests/images/%s.lut' % op, 'wb') as f: | ||||||
|  |                 f.write(lut) | ||||||
|  | 
 | ||||||
|  |     #create_lut() | ||||||
|  |     def test_lut(self): | ||||||
|  |         for op in ('corner', 'dilation4', 'dilation8', 'erosion4', 'erosion8', 'edge'): | ||||||
|  |             lb = ImageMorph.LutBuilder(op_name=op) | ||||||
|  |             lut = lb.build_lut() | ||||||
|  |             with open('Tests/images/%s.lut' % op , 'rb') as f: | ||||||
|  |                 self.assertEqual(lut, bytearray(f.read())) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     # Test the named patterns | ||||||
|  |     def test_erosion8(self): | ||||||
|  |         # erosion8 | ||||||
|  |         mop = ImageMorph.MorphOp(op_name='erosion8') | ||||||
|  |         count,Aout = mop.apply(self.A) | ||||||
|  |         self.assertEqual(count,8) | ||||||
|  |         self.assert_img_equal_img_string(Aout, | ||||||
|  |                                          """ | ||||||
|  |                                          ....... | ||||||
|  |                                          ....... | ||||||
|  |                                          ....... | ||||||
|  |                                          ...1... | ||||||
|  |                                          ....... | ||||||
|  |                                          ....... | ||||||
|  |                                          ....... | ||||||
|  |                                          """) | ||||||
|  | 
 | ||||||
|  |     def test_dialation8(self): | ||||||
|  |         # dialation8 | ||||||
|  |         mop = ImageMorph.MorphOp(op_name='dilation8') | ||||||
|  |         count,Aout = mop.apply(self.A) | ||||||
|  |         self.assertEqual(count,16) | ||||||
|  |         self.assert_img_equal_img_string(Aout, | ||||||
|  |                                          """ | ||||||
|  |                                          ....... | ||||||
|  |                                          .11111. | ||||||
|  |                                          .11111. | ||||||
|  |                                          .11111. | ||||||
|  |                                          .11111. | ||||||
|  |                                          .11111. | ||||||
|  |                                          ....... | ||||||
|  |                                          """) | ||||||
|  | 
 | ||||||
|  |     def test_erosion4(self): | ||||||
|  |         # erosion4 | ||||||
|  |         mop = ImageMorph.MorphOp(op_name='dilation4') | ||||||
|  |         count,Aout = mop.apply(self.A) | ||||||
|  |         self.assertEqual(count,12) | ||||||
|  |         self.assert_img_equal_img_string(Aout, | ||||||
|  |                                          """ | ||||||
|  |                                          ....... | ||||||
|  |                                          ..111.. | ||||||
|  |                                          .11111. | ||||||
|  |                                          .11111. | ||||||
|  |                                          .11111. | ||||||
|  |                                          ..111.. | ||||||
|  |                                          ....... | ||||||
|  |                                          """) | ||||||
|  | 
 | ||||||
|  |     def test_edge(self): | ||||||
|  |         # edge  | ||||||
|  |         mop = ImageMorph.MorphOp(op_name='edge') | ||||||
|  |         count,Aout = mop.apply(self.A) | ||||||
|  |         self.assertEqual(count,1) | ||||||
|  |         self.assert_img_equal_img_string(Aout, | ||||||
|  |                                          """ | ||||||
|  |                                          ....... | ||||||
|  |                                          ....... | ||||||
|  |                                          ..111.. | ||||||
|  |                                          ..1.1.. | ||||||
|  |                                          ..111.. | ||||||
|  |                                          ....... | ||||||
|  |                                          ....... | ||||||
|  |                                          """) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def test_corner(self): | ||||||
|  |         # Create a corner detector pattern | ||||||
|  |         mop = ImageMorph.MorphOp(patterns = ['1:(... ... ...)->0', | ||||||
|  |                                              '4:(00. 01. ...)->1']) | ||||||
|  |         count,Aout = mop.apply(self.A) | ||||||
|  |         self.assertEqual(count,5) | ||||||
|  |         self.assert_img_equal_img_string(Aout, | ||||||
|  |                                          """ | ||||||
|  |                                          ....... | ||||||
|  |                                          ....... | ||||||
|  |                                          ..1.1.. | ||||||
|  |                                          ....... | ||||||
|  |                                          ..1.1.. | ||||||
|  |                                          ....... | ||||||
|  |                                          ....... | ||||||
|  |                                          """) | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         # Test the coordinate counting with the same operator | ||||||
|  |         coords = mop.match(self.A) | ||||||
|  |         self.assertEqual(len(coords), 4) | ||||||
|  |         self.assertEqual(tuple(coords), | ||||||
|  |                      ((2,2),(4,2),(2,4),(4,4))) | ||||||
|  | 
 | ||||||
|  |         coords = mop.get_on_pixels(Aout) | ||||||
|  |         self.assertEqual(len(coords), 4) | ||||||
|  |         self.assertEqual(tuple(coords), | ||||||
|  |                      ((2,2),(4,2),(2,4),(4,4))) | ||||||
|  | @ -4,7 +4,7 @@ from PIL import Image | ||||||
| from PIL import ImageOps | from PIL import ImageOps | ||||||
| from PIL import ImageFilter | from PIL import ImageFilter | ||||||
| 
 | 
 | ||||||
| im = Image.open("Images/lena.ppm") | im = Image.open("Tests/images/lena.ppm") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestImageOpsUsm(PillowTestCase): | class TestImageOpsUsm(PillowTestCase): | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ import locale | ||||||
| 
 | 
 | ||||||
| # one of string.whitespace is not freely convertable into ascii. | # one of string.whitespace is not freely convertable into ascii. | ||||||
| 
 | 
 | ||||||
| path = "Images/lena.jpg" | path = "Tests/images/lena.jpg" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestLocale(PillowTestCase): | class TestLocale(PillowTestCase): | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ from PIL import Image | ||||||
| class TestPickle(PillowTestCase): | class TestPickle(PillowTestCase): | ||||||
| 
 | 
 | ||||||
|     def helper_pickle_file(self, pickle, protocol=0): |     def helper_pickle_file(self, pickle, protocol=0): | ||||||
|         im = Image.open('Images/lena.jpg') |         im = Image.open('Tests/images/lena.jpg') | ||||||
|         filename = self.tempfile('temp.pkl') |         filename = self.tempfile('temp.pkl') | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|  | @ -19,7 +19,7 @@ class TestPickle(PillowTestCase): | ||||||
|         self.assertEqual(im, loaded_im) |         self.assertEqual(im, loaded_im) | ||||||
| 
 | 
 | ||||||
|     def helper_pickle_string( |     def helper_pickle_string( | ||||||
|             self, pickle, protocol=0, file='Images/lena.jpg'): |             self, pickle, protocol=0, file='Tests/images/lena.jpg'): | ||||||
|         im = Image.open(file) |         im = Image.open(file) | ||||||
| 
 | 
 | ||||||
|         # Act |         # Act | ||||||
|  |  | ||||||
|  | @ -215,7 +215,7 @@ def lena(mode="RGB", cache={}): | ||||||
|     im = cache.get(mode) |     im = cache.get(mode) | ||||||
|     if im is None: |     if im is None: | ||||||
|         if mode == "RGB": |         if mode == "RGB": | ||||||
|             im = Image.open("Images/lena.ppm") |             im = Image.open("Tests/images/lena.ppm") | ||||||
|         elif mode == "F": |         elif mode == "F": | ||||||
|             im = lena("L").convert(mode) |             im = lena("L").convert(mode) | ||||||
|         elif mode[:4] == "I;16": |         elif mode[:4] == "I;16": | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ try: | ||||||
| except: | except: | ||||||
|     format = "PNG" |     format = "PNG" | ||||||
| 
 | 
 | ||||||
| im = Image.open("Images/lena.ppm") | im = Image.open("Tests/images/lena.ppm") | ||||||
| im.load() | im.load() | ||||||
| 
 | 
 | ||||||
| queue = queue.Queue() | queue = queue.Queue() | ||||||
|  |  | ||||||
							
								
								
									
										292
									
								
								Tk/README.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,292 @@ | ||||||
|  | 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. | ||||||
|  | 
 | ||||||
|  | .. 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``:: | ||||||
|  | 
 | ||||||
|  |     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 | ||||||
							
								
								
									
										108
									
								
								Tk/booster.txt
									
									
									
									
									
								
							
							
						
						|  | @ -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 |  | ||||||
|  | @ -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 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
							
								
								
									
										149
									
								
								Tk/pilbitmap.txt
									
									
									
									
									
								
							
							
						
						|  | @ -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 |  | ||||||
							
								
								
									
										303
									
								
								_imagingmorph.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,303 @@ | ||||||
|  | /*
 | ||||||
|  |  * The Python Imaging Library | ||||||
|  |  * | ||||||
|  |  * A binary morphology add-on for the Python Imaging Library | ||||||
|  |  * | ||||||
|  |  * History: | ||||||
|  |  *   2014-06-04 Initial version. | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com> | ||||||
|  |  * | ||||||
|  |  * See the README file for information on usage and redistribution. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "Python.h" | ||||||
|  | #include "Imaging.h" | ||||||
|  | #include "py3.h" | ||||||
|  | 
 | ||||||
|  | #define LUT_SIZE (1<<9) | ||||||
|  | 
 | ||||||
|  | /* Apply a morphologic LUT to a binary image. Outputs a
 | ||||||
|  |    a new binary image. | ||||||
|  | 
 | ||||||
|  |    Expected parameters: | ||||||
|  | 
 | ||||||
|  |       1. a LUT - a 512 byte size lookup table. | ||||||
|  |       2. an input Imaging image id. | ||||||
|  |       3. an output Imaging image id | ||||||
|  | 
 | ||||||
|  |    Returns number of changed pixels. | ||||||
|  | */ | ||||||
|  | static PyObject* | ||||||
|  | apply(PyObject *self, PyObject* args) | ||||||
|  | { | ||||||
|  |     const char *lut; | ||||||
|  |     PyObject *py_lut; | ||||||
|  |     Py_ssize_t lut_len, i0, i1; | ||||||
|  |     Imaging imgin, imgout; | ||||||
|  |     int width, height; | ||||||
|  |     int row_idx, col_idx; | ||||||
|  |     UINT8 **inrows, **outrows; | ||||||
|  |     int num_changed_pixels = 0; | ||||||
|  | 
 | ||||||
|  |     if (!PyArg_ParseTuple(args, "Onn", &py_lut, &i0, &i1)) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!PyBytes_Check(py_lut)) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lut_len = PyBytes_Size(py_lut); | ||||||
|  | 
 | ||||||
|  |     if (lut_len < LUT_SIZE) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lut = PyBytes_AsString(py_lut); | ||||||
|  | 
 | ||||||
|  |     imgin = (Imaging) i0; | ||||||
|  |     imgout = (Imaging) i1; | ||||||
|  |     width = imgin->xsize; | ||||||
|  |     height = imgin->ysize; | ||||||
|  | 
 | ||||||
|  |     if (imgin->type != IMAGING_TYPE_UINT8 && | ||||||
|  |         imgin->bands != 1) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "Unsupported image type"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     if (imgout->type != IMAGING_TYPE_UINT8 && | ||||||
|  |         imgout->bands != 1) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "Unsupported image type"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     inrows = imgin->image8; | ||||||
|  |     outrows = imgout->image8; | ||||||
|  | 
 | ||||||
|  |     for (row_idx=0; row_idx < height; row_idx++) { | ||||||
|  |         UINT8 *outrow = outrows[row_idx]; | ||||||
|  |         UINT8 *inrow = inrows[row_idx]; | ||||||
|  |         UINT8 *prow, *nrow; /* Previous and next row */ | ||||||
|  | 
 | ||||||
|  |         /* zero boundary conditions. TBD support other modes */ | ||||||
|  |         outrow[0] = outrow[width-1] = 0; | ||||||
|  |         if (row_idx==0 || row_idx == height-1) { | ||||||
|  |             for(col_idx=0; col_idx<width; col_idx++) | ||||||
|  |                 outrow[col_idx] = 0; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         prow = inrows[row_idx-1]; | ||||||
|  |         nrow = inrows[row_idx+1]; | ||||||
|  | 
 | ||||||
|  |         for (col_idx=1; col_idx<width-1; col_idx++) { | ||||||
|  |             int cim = col_idx-1; | ||||||
|  |             int cip = col_idx+1; | ||||||
|  |             unsigned char b0 = prow[cim] &1; | ||||||
|  |             unsigned char b1 = prow[col_idx]&1; | ||||||
|  |             unsigned char b2 = prow[cip]&1; | ||||||
|  | 
 | ||||||
|  |             unsigned char b3 = inrow[cim]&1; | ||||||
|  |             unsigned char b4 = inrow[col_idx]&1; | ||||||
|  |             unsigned char b5 = inrow[cip]&1; | ||||||
|  | 
 | ||||||
|  |             unsigned char b6 = nrow[cim]&1; | ||||||
|  |             unsigned char b7 = nrow[col_idx]&1; | ||||||
|  |             unsigned char b8 = nrow[cip]&1; | ||||||
|  | 
 | ||||||
|  |             int lut_idx = (b0  | ||||||
|  |                            |(b1 << 1) | ||||||
|  |                            |(b2 << 2) | ||||||
|  |                            |(b3 << 3) | ||||||
|  |                            |(b4 << 4) | ||||||
|  |                            |(b5 << 5) | ||||||
|  |                            |(b6 << 6) | ||||||
|  |                            |(b7 << 7) | ||||||
|  |                            |(b8 << 8)); | ||||||
|  |             outrow[col_idx] = 255*(lut[lut_idx]&1); | ||||||
|  |             num_changed_pixels += ((b4&1)!=(outrow[col_idx]&1)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return Py_BuildValue("i",num_changed_pixels); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Match a morphologic LUT to a binary image and return a list
 | ||||||
|  |    of the coordinates of all all matching pixels. | ||||||
|  | 
 | ||||||
|  |    Expected parameters: | ||||||
|  | 
 | ||||||
|  |       1. a LUT - a 512 byte size lookup table. | ||||||
|  |       2. an input Imaging image id. | ||||||
|  | 
 | ||||||
|  |    Returns list of matching pixels. | ||||||
|  | */ | ||||||
|  | static PyObject* | ||||||
|  | match(PyObject *self, PyObject* args) | ||||||
|  | { | ||||||
|  |     const char *lut; | ||||||
|  |     PyObject *py_lut; | ||||||
|  |     Py_ssize_t lut_len, i0; | ||||||
|  |     Imaging imgin; | ||||||
|  |     int width, height; | ||||||
|  |     int row_idx, col_idx; | ||||||
|  |     UINT8 **inrows; | ||||||
|  |     PyObject *ret = PyList_New(0); | ||||||
|  | 
 | ||||||
|  |     if (!PyArg_ParseTuple(args, "On", &py_lut, &i0)) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!PyBytes_Check(py_lut)) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lut_len = PyBytes_Size(py_lut); | ||||||
|  | 
 | ||||||
|  |     if (lut_len < LUT_SIZE) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lut = PyBytes_AsString(py_lut); | ||||||
|  |     imgin = (Imaging) i0; | ||||||
|  | 
 | ||||||
|  |     if (imgin->type != IMAGING_TYPE_UINT8 && | ||||||
|  |         imgin->bands != 1) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "Unsupported image type"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     inrows = imgin->image8; | ||||||
|  |     width = imgin->xsize; | ||||||
|  |     height = imgin->ysize; | ||||||
|  | 
 | ||||||
|  |     for (row_idx=1; row_idx < height-1; row_idx++) { | ||||||
|  |         UINT8 *inrow = inrows[row_idx]; | ||||||
|  |         UINT8 *prow, *nrow; | ||||||
|  | 
 | ||||||
|  |         prow = inrows[row_idx-1]; | ||||||
|  |         nrow = inrows[row_idx+1]; | ||||||
|  | 
 | ||||||
|  |         for (col_idx=1; col_idx<width-1; col_idx++) { | ||||||
|  |             int cim = col_idx-1; | ||||||
|  |             int cip = col_idx+1; | ||||||
|  |             unsigned char b0 = prow[cim] &1; | ||||||
|  |             unsigned char b1 = prow[col_idx]&1; | ||||||
|  |             unsigned char b2 = prow[cip]&1; | ||||||
|  | 
 | ||||||
|  |             unsigned char b3 = inrow[cim]&1; | ||||||
|  |             unsigned char b4 = inrow[col_idx]&1; | ||||||
|  |             unsigned char b5 = inrow[cip]&1; | ||||||
|  | 
 | ||||||
|  |             unsigned char b6 = nrow[cim]&1; | ||||||
|  |             unsigned char b7 = nrow[col_idx]&1; | ||||||
|  |             unsigned char b8 = nrow[cip]&1; | ||||||
|  | 
 | ||||||
|  |             int lut_idx = (b0  | ||||||
|  |                            |(b1 << 1) | ||||||
|  |                            |(b2 << 2) | ||||||
|  |                            |(b3 << 3) | ||||||
|  |                            |(b4 << 4) | ||||||
|  |                            |(b5 << 5) | ||||||
|  |                            |(b6 << 6) | ||||||
|  |                            |(b7 << 7) | ||||||
|  |                            |(b8 << 8)); | ||||||
|  |             if (lut[lut_idx]) { | ||||||
|  |                 PyObject *coordObj = Py_BuildValue("(nn)",col_idx,row_idx); | ||||||
|  |                 PyList_Append(ret, coordObj); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Return a list of the coordinates of all turned on pixels in an image.
 | ||||||
|  |    May be used to extract features after a sequence of MorphOp's were applied. | ||||||
|  |    This is faster than match as only 1x1 lookup is made. | ||||||
|  | */ | ||||||
|  | static PyObject* | ||||||
|  | get_on_pixels(PyObject *self, PyObject* args) | ||||||
|  | { | ||||||
|  |     Py_ssize_t i0; | ||||||
|  |     Imaging img; | ||||||
|  |     UINT8 **rows; | ||||||
|  |     int row_idx, col_idx; | ||||||
|  |     int width, height; | ||||||
|  |     PyObject *ret = PyList_New(0); | ||||||
|  |    | ||||||
|  |     if (!PyArg_ParseTuple(args, "n", &i0)) { | ||||||
|  |         PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem"); | ||||||
|  | 
 | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |     img = (Imaging) i0; | ||||||
|  |     rows = img->image8; | ||||||
|  |     width = img->xsize; | ||||||
|  |     height = img->ysize; | ||||||
|  |    | ||||||
|  |     for (row_idx=0; row_idx < height; row_idx++) { | ||||||
|  |         UINT8 *row = rows[row_idx]; | ||||||
|  |         for (col_idx=0; col_idx<width; col_idx++) { | ||||||
|  |             if (row[col_idx]) { | ||||||
|  |                 PyObject *coordObj = Py_BuildValue("(nn)",col_idx,row_idx); | ||||||
|  |                 PyList_Append(ret, coordObj); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | setup_module(PyObject* m) | ||||||
|  | { | ||||||
|  |     PyObject* d = PyModule_GetDict(m); | ||||||
|  | 
 | ||||||
|  |     PyDict_SetItemString(d, "__version", PyUnicode_FromString("0.1")); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyMethodDef functions[] = { | ||||||
|  |     /* Functions */ | ||||||
|  |     {"apply", (PyCFunction)apply, 1}, | ||||||
|  |     {"get_on_pixels", (PyCFunction)get_on_pixels, 1}, | ||||||
|  |     {"match", (PyCFunction)match, 1}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #if PY_VERSION_HEX >= 0x03000000 | ||||||
|  | PyMODINIT_FUNC | ||||||
|  | PyInit__imagingmorph(void) { | ||||||
|  |     PyObject* m; | ||||||
|  | 
 | ||||||
|  |     static PyModuleDef module_def = { | ||||||
|  |         PyModuleDef_HEAD_INIT, | ||||||
|  |         "_imagingmorph",         /* m_name */ | ||||||
|  |         "A module for doing image morphology",               /* m_doc */ | ||||||
|  |         -1,                 /* m_size */ | ||||||
|  |         functions,          /* m_methods */ | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     m = PyModule_Create(&module_def); | ||||||
|  | 
 | ||||||
|  |     if (setup_module(m) < 0) | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     return m; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | PyMODINIT_FUNC | ||||||
|  | init_imagingmorph(void) | ||||||
|  | { | ||||||
|  |     PyObject* m = Py_InitModule("_imagingmorph", functions); | ||||||
|  |     setup_module(m); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
							
								
								
									
										6
									
								
								decode.c
									
									
									
									
									
								
							
							
						
						|  | @ -797,8 +797,9 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) | ||||||
|     int reduce = 0; |     int reduce = 0; | ||||||
|     int layers = 0; |     int layers = 0; | ||||||
|     int fd = -1; |     int fd = -1; | ||||||
|     if (!PyArg_ParseTuple(args, "ss|iii", &mode, &format, |     PY_LONG_LONG length = -1; | ||||||
|                           &reduce, &layers, &fd)) |     if (!PyArg_ParseTuple(args, "ss|iiiL", &mode, &format, | ||||||
|  |                           &reduce, &layers, &fd, &length)) | ||||||
|         return NULL; |         return NULL; | ||||||
| 
 | 
 | ||||||
|     if (strcmp(format, "j2k") == 0) |     if (strcmp(format, "j2k") == 0) | ||||||
|  | @ -821,6 +822,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) | ||||||
|     context = (JPEG2KDECODESTATE *)decoder->state.context; |     context = (JPEG2KDECODESTATE *)decoder->state.context; | ||||||
| 
 | 
 | ||||||
|     context->fd = fd; |     context->fd = fd; | ||||||
|  |     context->length = (off_t)length; | ||||||
|     context->format = codec_format; |     context->format = codec_format; | ||||||
|     context->reduce = reduce; |     context->reduce = reduce; | ||||||
|     context->layers = layers; |     context->layers = layers; | ||||||
|  |  | ||||||
|  | @ -2,15 +2,16 @@ | ||||||
| # install openjpeg | # install openjpeg | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if [ ! -f openjpeg-2.0.0.tar.gz ]; then | if [ ! -f openjpeg-2.1.0.tar.gz ]; then | ||||||
|     wget 'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz'  |     wget 'http://iweb.dl.sourceforge.net/project/openjpeg.mirror/2.1.0/openjpeg-2.1.0.tar.gz' | ||||||
|  | 
 | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| rm -r openjpeg-2.0.0 | rm -r openjpeg-2.1.0 | ||||||
| tar -xvzf openjpeg-2.0.0.tar.gz | 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 | cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -73,7 +73,7 @@ Many of Pillow's features require external libraries: | ||||||
| 
 | 
 | ||||||
| * **openjpeg** provides JPEG 2000 functionality. | * **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 | If the prerequisites are installed in the standard library locations for your | ||||||
| machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration | machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
|  * Copyright (c) 2014 by Alastair Houghton |  * Copyright (c) 2014 by Alastair Houghton | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <openjpeg-2.0/openjpeg.h> | #include <openjpeg.h> | ||||||
| 
 | 
 | ||||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||||
| /* Decoder								*/ | /* Decoder								*/ | ||||||
|  | @ -20,6 +20,9 @@ typedef struct { | ||||||
|     /* File descriptor, if available; otherwise, -1 */ |     /* File descriptor, if available; otherwise, -1 */ | ||||||
|     int fd; |     int fd; | ||||||
| 
 | 
 | ||||||
|  |     /* Length of data, if available; otherwise, -1 */ | ||||||
|  |     off_t length; | ||||||
|  | 
 | ||||||
|     /* Specify the desired format */ |     /* Specify the desired format */ | ||||||
|     OPJ_CODEC_FORMAT format; |     OPJ_CODEC_FORMAT format; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -517,7 +517,21 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, | ||||||
|     opj_stream_set_read_function(stream, j2k_read); |     opj_stream_set_read_function(stream, j2k_read); | ||||||
|     opj_stream_set_skip_function(stream, j2k_skip); |     opj_stream_set_skip_function(stream, j2k_skip); | ||||||
| 
 | 
 | ||||||
|  |     /* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */ | ||||||
|  | #ifndef OPJ_VERSION_MAJOR | ||||||
|     opj_stream_set_user_data(stream, decoder); |     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 */ |     /* Setup decompression context */ | ||||||
|     context->error_msg = NULL; |     context->error_msg = NULL; | ||||||
|  |  | ||||||
|  | @ -259,7 +259,12 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, | ||||||
|     opj_stream_set_skip_function(stream, j2k_skip); |     opj_stream_set_skip_function(stream, j2k_skip); | ||||||
|     opj_stream_set_seek_function(stream, j2k_seek); |     opj_stream_set_seek_function(stream, j2k_seek); | ||||||
| 
 | 
 | ||||||
|  |     /* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */ | ||||||
|  | #ifndef OPJ_VERSION_MAJOR | ||||||
|     opj_stream_set_user_data(stream, encoder); |     opj_stream_set_user_data(stream, encoder); | ||||||
|  | #else | ||||||
|  |     opj_stream_set_user_data(stream, encoder, NULL); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|     /* Setup an opj_image */ |     /* Setup an opj_image */ | ||||||
|     if (strcmp (im->mode, "L") == 0) { |     if (strcmp (im->mode, "L") == 0) { | ||||||
|  |  | ||||||
|  | @ -13,11 +13,6 @@ | ||||||
| #include <tiff.h> | #include <tiff.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifndef _UNISTD_H |  | ||||||
| #include <unistd.h> |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #ifndef min | #ifndef min | ||||||
| #define min(x,y) (( x > y ) ? y : x ) | #define min(x,y) (( x > y ) ? y : x ) | ||||||
| #define max(x,y) (( x < y ) ? y : x ) | #define max(x,y) (( x < y ) ? y : x ) | ||||||
|  | @ -43,11 +38,10 @@ extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int | ||||||
| extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); | 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.*/ |    Trace debugging | ||||||
| #define TRACE(args) |    legacy, don't enable for python 3.x, unicode issues.  | ||||||
| #else | */ | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
| #define VA_ARGS(...)	__VA_ARGS__ | #define VA_ARGS(...)	__VA_ARGS__ | ||||||
|  | @ -56,8 +50,5 @@ extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); | ||||||
| 
 | 
 | ||||||
| #define TRACE(args) | #define TRACE(args) | ||||||
| 
 | 
 | ||||||
| #endif /* _MSC_VER */ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								selftest.py
									
									
									
									
									
								
							
							
						
						|  | @ -49,13 +49,13 @@ def testimage(): | ||||||
| 
 | 
 | ||||||
|     Or open existing files: |     Or open existing files: | ||||||
| 
 | 
 | ||||||
|     >>> im = Image.open(os.path.join(ROOT, "Images/lena.gif")) |     >>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.gif")) | ||||||
|     >>> _info(im) |     >>> _info(im) | ||||||
|     ('GIF', 'P', (128, 128)) |     ('GIF', 'P', (128, 128)) | ||||||
|     >>> _info(Image.open(os.path.join(ROOT, "Images/lena.ppm"))) |     >>> _info(Image.open(os.path.join(ROOT, "Tests/images/lena.ppm"))) | ||||||
|     ('PPM', 'RGB', (128, 128)) |     ('PPM', 'RGB', (128, 128)) | ||||||
|     >>> try: |     >>> try: | ||||||
|     ...  _info(Image.open(os.path.join(ROOT, "Images/lena.jpg"))) |     ...  _info(Image.open(os.path.join(ROOT, "Tests/images/lena.jpg"))) | ||||||
|     ... except IOError as v: |     ... except IOError as v: | ||||||
|     ...  print(v) |     ...  print(v) | ||||||
|     ('JPEG', 'RGB', (128, 128)) |     ('JPEG', 'RGB', (128, 128)) | ||||||
|  | @ -63,7 +63,7 @@ def testimage(): | ||||||
|     PIL doesn't actually load the image data until it's needed, |     PIL doesn't actually load the image data until it's needed, | ||||||
|     or you call the "load" method: |     or you call the "load" method: | ||||||
| 
 | 
 | ||||||
|     >>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm")) |     >>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.ppm")) | ||||||
|     >>> print(im.im) # internal image attribute |     >>> print(im.im) # internal image attribute | ||||||
|     None |     None | ||||||
|     >>> a = im.load() |     >>> a = im.load() | ||||||
|  | @ -73,7 +73,7 @@ def testimage(): | ||||||
|     You can apply many different operations on images.  Most |     You can apply many different operations on images.  Most | ||||||
|     operations return a new image: |     operations return a new image: | ||||||
| 
 | 
 | ||||||
|     >>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm")) |     >>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.ppm")) | ||||||
|     >>> _info(im.convert("L")) |     >>> _info(im.convert("L")) | ||||||
|     (None, 'L', (128, 128)) |     (None, 'L', (128, 128)) | ||||||
|     >>> _info(im.copy()) |     >>> _info(im.copy()) | ||||||
|  |  | ||||||
							
								
								
									
										54
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						|  | @ -337,14 +337,23 @@ class pil_build_ext(build_ext): | ||||||
|         _add_directory(include_dirs, "/usr/include") |         _add_directory(include_dirs, "/usr/include") | ||||||
| 
 | 
 | ||||||
|         # on Windows, look for the OpenJPEG libraries in the location that |         # 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": |         if sys.platform == "win32": | ||||||
|  |             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, |                 _add_directory(library_dirs, | ||||||
|                            os.path.join(os.environ.get("ProgramFiles", ""), |                                os.path.join(best_path, 'lib')) | ||||||
|                                         "OpenJPEG 2.0", "lib")) |  | ||||||
|                 _add_directory(include_dirs, |                 _add_directory(include_dirs, | ||||||
|                            os.path.join(os.environ.get("ProgramFiles", ""), |                                os.path.join(best_path, 'include')) | ||||||
|                                         "OpenJPEG 2.0", "include")) |  | ||||||
| 
 | 
 | ||||||
|         # |         # | ||||||
|         # insert new dirs *before* default libs, to avoid conflicts |         # insert new dirs *before* default libs, to avoid conflicts | ||||||
|  | @ -374,10 +383,29 @@ class pil_build_ext(build_ext): | ||||||
|                         _find_library_file(self, "libjpeg")): |                         _find_library_file(self, "libjpeg")): | ||||||
|                     feature.jpeg = "libjpeg"  # alternative name |                     feature.jpeg = "libjpeg"  # alternative name | ||||||
| 
 | 
 | ||||||
|  |         feature.openjpeg_version = None | ||||||
|         if feature.want('jpeg2000'): |         if feature.want('jpeg2000'): | ||||||
|             if _find_include_file(self, "openjpeg-2.0/openjpeg.h"): |             best_version = None | ||||||
|                 if _find_library_file(self, "openjp2"): |             best_path = None | ||||||
|                     feature.jpeg2000 = "openjp2" |              | ||||||
|  |             # 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 | ||||||
|  |                 # <openjpeg.h> 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 feature.want('tiff'): | ||||||
|             if _find_library_file(self, "tiff"): |             if _find_library_file(self, "tiff"): | ||||||
|  | @ -543,6 +571,9 @@ class pil_build_ext(build_ext): | ||||||
|         if os.path.isfile("_imagingmath.c"): |         if os.path.isfile("_imagingmath.c"): | ||||||
|             exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"])) |             exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"])) | ||||||
| 
 | 
 | ||||||
|  |         if os.path.isfile("_imagingmorph.c"): | ||||||
|  |             exts.append(Extension("PIL._imagingmorph", ["_imagingmorph.c"])) | ||||||
|  | 
 | ||||||
|         self.extensions[:] = exts |         self.extensions[:] = exts | ||||||
| 
 | 
 | ||||||
|         build_ext.build_extensions(self) |         build_ext.build_extensions(self) | ||||||
|  | @ -572,7 +603,7 @@ class pil_build_ext(build_ext): | ||||||
|         options = [ |         options = [ | ||||||
|             (feature.tcl and feature.tk, "TKINTER"), |             (feature.tcl and feature.tk, "TKINTER"), | ||||||
|             (feature.jpeg, "JPEG"), |             (feature.jpeg, "JPEG"), | ||||||
|             (feature.jpeg2000, "OPENJPEG (JPEG2000)"), |             (feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version), | ||||||
|             (feature.zlib, "ZLIB (PNG/ZIP)"), |             (feature.zlib, "ZLIB (PNG/ZIP)"), | ||||||
|             (feature.tiff, "LIBTIFF"), |             (feature.tiff, "LIBTIFF"), | ||||||
|             (feature.freetype, "FREETYPE2"), |             (feature.freetype, "FREETYPE2"), | ||||||
|  | @ -583,7 +614,10 @@ class pil_build_ext(build_ext): | ||||||
|         all = 1 |         all = 1 | ||||||
|         for option in options: |         for option in options: | ||||||
|             if option[0]: |             if option[0]: | ||||||
|                 print("--- %s support available" % option[1]) |                 version = '' | ||||||
|  |                 if len(option) >= 3 and option[2]: | ||||||
|  |                     version = ' (%s)' % option[2] | ||||||
|  |                 print("--- %s support available%s" % (option[1], version)) | ||||||
|             else: |             else: | ||||||
|                 print("*** %s support not available" % option[1]) |                 print("*** %s support not available" % option[1]) | ||||||
|                 if option[1] == "TKINTER" and _tkinter: |                 if option[1] == "TKINTER" and _tkinter: | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								test-installed.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						|  | @ -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() | ||||||