mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 16:07:30 +03:00 
			
		
		
		
	merge from master
This commit is contained in:
		
						commit
						caf27c6908
					
				
							
								
								
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -6,3 +6,12 @@ dist | ||||||
| *.so | *.so | ||||||
| docs/_build | docs/_build | ||||||
| *~ | *~ | ||||||
|  | 
 | ||||||
|  | # Vim cruft | ||||||
|  | .*.swp | ||||||
|  | 
 | ||||||
|  | #emacs | ||||||
|  | *~ | ||||||
|  | \#*# | ||||||
|  | .#* | ||||||
|  | 
 | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								.travis.yml
									
									
									
									
									
								
							|  | @ -4,19 +4,32 @@ language: python | ||||||
| virtualenv: | virtualenv: | ||||||
|   system_site_packages: true |   system_site_packages: true | ||||||
| 
 | 
 | ||||||
|  | notifications: | ||||||
|  |   irc: "chat.freenode.net#pil" | ||||||
|  | 
 | ||||||
| python: | python: | ||||||
|   - 2.6 |   - 2.6 | ||||||
|   - 2.7 |   - 2.7 | ||||||
|   - 3.2 |   - 3.2 | ||||||
|   - 3.3 |   - 3.3 | ||||||
|  |   - "pypy" | ||||||
| 
 | 
 | ||||||
| install:  | install:  | ||||||
|   - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript libffi-dev" |   - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" | ||||||
|   - "pip install cffi" |   - "pip install cffi" | ||||||
| 
 | 
 | ||||||
|  |   # webp   | ||||||
|  |   - pushd depends && ./install_webp.sh && popd | ||||||
|  | 
 | ||||||
|  |   # openjpeg | ||||||
|  |   - pushd depends && ./install_openjpeg.sh && popd | ||||||
| 
 | 
 | ||||||
| script: | script: | ||||||
|   - python setup.py clean |   - python setup.py clean | ||||||
|   - python setup.py build_ext --inplace |   - python setup.py build_ext --inplace | ||||||
|   - python selftest.py |   - python selftest.py | ||||||
|   - python Tests/run.py |   - python Tests/run.py | ||||||
|  | 
 | ||||||
|  | matrix: | ||||||
|  |   allow_failures: | ||||||
|  |     - python: "pypy" | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								CHANGES.rst
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								CHANGES.rst
									
									
									
									
									
								
							|  | @ -4,6 +4,21 @@ Changelog (Pillow) | ||||||
| 2.4.0 (unreleased) | 2.4.0 (unreleased) | ||||||
| ------------------ | ------------------ | ||||||
| 
 | 
 | ||||||
|  | - Improved icns support | ||||||
|  |   [al45tair] | ||||||
|  | 
 | ||||||
|  | - Fix libtiff leaking open files, fixes #580 | ||||||
|  |   [wiredfool] | ||||||
|  | 
 | ||||||
|  | - Fixes for Jpeg encoding in Python 3, fixes #577 | ||||||
|  |   [wiredfool] | ||||||
|  | 
 | ||||||
|  | - Added support for JPEG 2000 | ||||||
|  |   [al45tair] | ||||||
|  | 
 | ||||||
|  | - Add more detailed error messages to Image.py  | ||||||
|  |   [larsmans] | ||||||
|  | 
 | ||||||
| - Avoid conflicting _expand functions in PIL & MINGW, fixes #538 | - Avoid conflicting _expand functions in PIL & MINGW, fixes #538 | ||||||
|   [aclark] |   [aclark] | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								Images/pillow.icns
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Images/pillow.icns
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Images/pillow.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Images/pillow.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 100 KiB | 
|  | @ -10,12 +10,17 @@ | ||||||
| # Copyright (c) 2004 by Bob Ippolito. | # Copyright (c) 2004 by Bob Ippolito. | ||||||
| # Copyright (c) 2004 by Secret Labs. | # Copyright (c) 2004 by Secret Labs. | ||||||
| # Copyright (c) 2004 by Fredrik Lundh. | # Copyright (c) 2004 by Fredrik Lundh. | ||||||
|  | # Copyright (c) 2014 by Alastair Houghton. | ||||||
| # | # | ||||||
| # See the README file for information on usage and redistribution. | # See the README file for information on usage and redistribution. | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
| from PIL import Image, ImageFile, _binary | from PIL import Image, ImageFile, PngImagePlugin, _binary | ||||||
| import struct | import struct, io | ||||||
|  | 
 | ||||||
|  | enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') | ||||||
|  | if enable_jpeg2k: | ||||||
|  |     from PIL import Jpeg2KImagePlugin | ||||||
| 
 | 
 | ||||||
| i8 = _binary.i8 | i8 = _binary.i8 | ||||||
| 
 | 
 | ||||||
|  | @ -40,14 +45,15 @@ def read_32(fobj, start_length, size): | ||||||
|     """ |     """ | ||||||
|     (start, length) = start_length |     (start, length) = start_length | ||||||
|     fobj.seek(start) |     fobj.seek(start) | ||||||
|     sizesq = size[0] * size[1] |     pixel_size = (size[0] * size[2], size[1] * size[2]) | ||||||
|  |     sizesq = pixel_size[0] * pixel_size[1] | ||||||
|     if length == sizesq * 3: |     if length == sizesq * 3: | ||||||
|         # uncompressed ("RGBRGBGB") |         # uncompressed ("RGBRGBGB") | ||||||
|         indata = fobj.read(length) |         indata = fobj.read(length) | ||||||
|         im = Image.frombuffer("RGB", size, indata, "raw", "RGB", 0, 1) |         im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1) | ||||||
|     else: |     else: | ||||||
|         # decode image |         # decode image | ||||||
|         im = Image.new("RGB", size, None) |         im = Image.new("RGB", pixel_size, None) | ||||||
|         for band_ix in range(3): |         for band_ix in range(3): | ||||||
|             data = [] |             data = [] | ||||||
|             bytesleft = sizesq |             bytesleft = sizesq | ||||||
|  | @ -72,7 +78,7 @@ def read_32(fobj, start_length, size): | ||||||
|                     "Error reading channel [%r left]" % bytesleft |                     "Error reading channel [%r left]" % bytesleft | ||||||
|                     ) |                     ) | ||||||
|             band = Image.frombuffer( |             band = Image.frombuffer( | ||||||
|                 "L", size, b"".join(data), "raw", "L", 0, 1 |                 "L", pixel_size, b"".join(data), "raw", "L", 0, 1 | ||||||
|                 ) |                 ) | ||||||
|             im.im.putband(band.im, band_ix) |             im.im.putband(band.im, band_ix) | ||||||
|     return {"RGB": im} |     return {"RGB": im} | ||||||
|  | @ -81,27 +87,80 @@ def read_mk(fobj, start_length, size): | ||||||
|     # Alpha masks seem to be uncompressed |     # Alpha masks seem to be uncompressed | ||||||
|     (start, length) = start_length |     (start, length) = start_length | ||||||
|     fobj.seek(start) |     fobj.seek(start) | ||||||
|  |     pixel_size = (size[0] * size[2], size[1] * size[2]) | ||||||
|  |     sizesq = pixel_size[0] * pixel_size[1] | ||||||
|     band = Image.frombuffer( |     band = Image.frombuffer( | ||||||
|         "L", size, fobj.read(size[0]*size[1]), "raw", "L", 0, 1 |         "L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1 | ||||||
|         ) |         ) | ||||||
|     return {"A": band} |     return {"A": band} | ||||||
| 
 | 
 | ||||||
|  | def read_png_or_jpeg2000(fobj, start_length, size): | ||||||
|  |     (start, length) = start_length | ||||||
|  |     fobj.seek(start) | ||||||
|  |     sig = fobj.read(12) | ||||||
|  |     if sig[:8] == b'\x89PNG\x0d\x0a\x1a\x0a': | ||||||
|  |         fobj.seek(start) | ||||||
|  |         im = PngImagePlugin.PngImageFile(fobj) | ||||||
|  |         return {"RGBA": im} | ||||||
|  |     elif sig[:4] == b'\xff\x4f\xff\x51' \ | ||||||
|  |         or sig[:4] == b'\x0d\x0a\x87\x0a' \ | ||||||
|  |         or sig == b'\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a': | ||||||
|  |         if not enable_jpeg2k: | ||||||
|  |             raise ValueError('Unsupported icon subimage format (rebuild PIL with JPEG 2000 support to fix this)') | ||||||
|  |         # j2k, jpc or j2c | ||||||
|  |         fobj.seek(start) | ||||||
|  |         jp2kstream = fobj.read(length) | ||||||
|  |         f = io.BytesIO(jp2kstream) | ||||||
|  |         im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) | ||||||
|  |         if im.mode != 'RGBA': | ||||||
|  |             im = im.convert('RGBA') | ||||||
|  |         return {"RGBA": im} | ||||||
|  |     else: | ||||||
|  |         raise ValueError('Unsupported icon subimage format') | ||||||
|  | 
 | ||||||
| class IcnsFile: | class IcnsFile: | ||||||
| 
 | 
 | ||||||
|     SIZES = { |     SIZES = { | ||||||
|         (128, 128): [ |         (512, 512, 2): [ | ||||||
|  |             (b'ic10', read_png_or_jpeg2000), | ||||||
|  |         ], | ||||||
|  |         (512, 512, 1): [ | ||||||
|  |             (b'ic09', read_png_or_jpeg2000), | ||||||
|  |         ], | ||||||
|  |         (256, 256, 2): [ | ||||||
|  |             (b'ic14', read_png_or_jpeg2000), | ||||||
|  |         ], | ||||||
|  |         (256, 256, 1): [ | ||||||
|  |             (b'ic08', read_png_or_jpeg2000), | ||||||
|  |         ], | ||||||
|  |         (128, 128, 2): [ | ||||||
|  |             (b'ic13', read_png_or_jpeg2000), | ||||||
|  |         ], | ||||||
|  |         (128, 128, 1): [ | ||||||
|  |             (b'ic07', read_png_or_jpeg2000), | ||||||
|             (b'it32', read_32t), |             (b'it32', read_32t), | ||||||
|             (b't8mk', read_mk), |             (b't8mk', read_mk), | ||||||
|         ], |         ], | ||||||
|         (48, 48): [ |         (64, 64, 1): [ | ||||||
|  |             (b'icp6', read_png_or_jpeg2000), | ||||||
|  |         ], | ||||||
|  |         (32, 32, 2): [ | ||||||
|  |             (b'ic12', read_png_or_jpeg2000), | ||||||
|  |         ], | ||||||
|  |         (48, 48, 1): [ | ||||||
|             (b'ih32', read_32), |             (b'ih32', read_32), | ||||||
|             (b'h8mk', read_mk), |             (b'h8mk', read_mk), | ||||||
|         ], |         ], | ||||||
|         (32, 32): [ |         (32, 32, 1): [ | ||||||
|  |             (b'icp5', read_png_or_jpeg2000), | ||||||
|             (b'il32', read_32), |             (b'il32', read_32), | ||||||
|             (b'l8mk', read_mk), |             (b'l8mk', read_mk), | ||||||
|         ], |         ], | ||||||
|         (16, 16): [ |         (16, 16, 2): [ | ||||||
|  |             (b'ic11', read_png_or_jpeg2000), | ||||||
|  |         ], | ||||||
|  |         (16, 16, 1): [ | ||||||
|  |             (b'icp4', read_png_or_jpeg2000), | ||||||
|             (b'is32', read_32), |             (b'is32', read_32), | ||||||
|             (b's8mk', read_mk), |             (b's8mk', read_mk), | ||||||
|         ], |         ], | ||||||
|  | @ -115,7 +174,7 @@ class IcnsFile: | ||||||
|         self.dct = dct = {} |         self.dct = dct = {} | ||||||
|         self.fobj = fobj |         self.fobj = fobj | ||||||
|         sig, filesize = nextheader(fobj) |         sig, filesize = nextheader(fobj) | ||||||
|         if sig != 'icns': |         if sig != b'icns': | ||||||
|             raise SyntaxError('not an icns file') |             raise SyntaxError('not an icns file') | ||||||
|         i = HEADERSIZE |         i = HEADERSIZE | ||||||
|         while i < filesize: |         while i < filesize: | ||||||
|  | @ -157,7 +216,14 @@ class IcnsFile: | ||||||
|     def getimage(self, size=None): |     def getimage(self, size=None): | ||||||
|         if size is None: |         if size is None: | ||||||
|             size = self.bestsize() |             size = self.bestsize() | ||||||
|  |         if len(size) == 2: | ||||||
|  |             size = (size[0], size[1], 1) | ||||||
|         channels = self.dataforsize(size) |         channels = self.dataforsize(size) | ||||||
|  | 
 | ||||||
|  |         im = channels.get('RGBA', None) | ||||||
|  |         if im: | ||||||
|  |             return im | ||||||
|  |          | ||||||
|         im = channels.get("RGB").copy() |         im = channels.get("RGB").copy() | ||||||
|         try: |         try: | ||||||
|             im.putalpha(channels["A"]) |             im.putalpha(channels["A"]) | ||||||
|  | @ -185,18 +251,29 @@ class IcnsImageFile(ImageFile.ImageFile): | ||||||
|     def _open(self): |     def _open(self): | ||||||
|         self.icns = IcnsFile(self.fp) |         self.icns = IcnsFile(self.fp) | ||||||
|         self.mode = 'RGBA' |         self.mode = 'RGBA' | ||||||
|         self.size = self.icns.bestsize() |         self.best_size = self.icns.bestsize() | ||||||
|  |         self.size = (self.best_size[0] * self.best_size[2], | ||||||
|  |                      self.best_size[1] * self.best_size[2]) | ||||||
|         self.info['sizes'] = self.icns.itersizes() |         self.info['sizes'] = self.icns.itersizes() | ||||||
|         # Just use this to see if it's loaded or not yet. |         # Just use this to see if it's loaded or not yet. | ||||||
|         self.tile = ('',) |         self.tile = ('',) | ||||||
| 
 | 
 | ||||||
|     def load(self): |     def load(self): | ||||||
|  |         if len(self.size) == 3: | ||||||
|  |             self.best_size = self.size | ||||||
|  |             self.size = (self.best_size[0] * self.best_size[2], | ||||||
|  |                          self.best_size[1] * self.best_size[2]) | ||||||
|  | 
 | ||||||
|         Image.Image.load(self) |         Image.Image.load(self) | ||||||
|         if not self.tile: |         if not self.tile: | ||||||
|             return |             return | ||||||
|         self.load_prepare() |         self.load_prepare() | ||||||
|         # This is likely NOT the best way to do it, but whatever. |         # This is likely NOT the best way to do it, but whatever. | ||||||
|         im = self.icns.getimage(self.size) |         im = self.icns.getimage(self.best_size) | ||||||
|  | 
 | ||||||
|  |         # If this is a PNG or JPEG 2000, it won't be loaded yet | ||||||
|  |         im.load() | ||||||
|  |          | ||||||
|         self.im = im.im |         self.im = im.im | ||||||
|         self.mode = im.mode |         self.mode = im.mode | ||||||
|         self.size = im.size |         self.size = im.size | ||||||
|  | @ -205,12 +282,18 @@ class IcnsImageFile(ImageFile.ImageFile): | ||||||
|         self.tile = () |         self.tile = () | ||||||
|         self.load_end() |         self.load_end() | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') | Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') | ||||||
| Image.register_extension("ICNS", '.icns') | Image.register_extension("ICNS", '.icns') | ||||||
| 
 | 
 | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     import os, sys |     import os, sys | ||||||
|  |     imf = IcnsImageFile(open(sys.argv[1], 'rb')) | ||||||
|  |     for size in imf.info['sizes']: | ||||||
|  |         imf.size = size | ||||||
|  |         imf.load() | ||||||
|  |         im = imf.im | ||||||
|  |         im.save('out-%s-%s-%s.png' % size) | ||||||
|     im = Image.open(open(sys.argv[1], "rb")) |     im = Image.open(open(sys.argv[1], "rb")) | ||||||
|     im.save("out.png") |     im.save("out.png") | ||||||
|  |     if sys.platform == 'windows': | ||||||
|         os.startfile("out.png") |         os.startfile("out.png") | ||||||
|  |  | ||||||
|  | @ -1965,7 +1965,7 @@ def fromarray(obj, mode=None): | ||||||
|     else: |     else: | ||||||
|         ndmax = 4 |         ndmax = 4 | ||||||
|     if ndim > ndmax: |     if ndim > ndmax: | ||||||
|         raise ValueError("Too many dimensions.") |         raise ValueError("Too many dimensions: %d > %d." % (ndim, ndmax)) | ||||||
| 
 | 
 | ||||||
|     size = shape[1], shape[0] |     size = shape[1], shape[0] | ||||||
|     if strides is not None: |     if strides is not None: | ||||||
|  | @ -2018,7 +2018,7 @@ def open(fp, mode="r"): | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     if mode != "r": |     if mode != "r": | ||||||
|         raise ValueError("bad mode") |         raise ValueError("bad mode %r" % mode) | ||||||
| 
 | 
 | ||||||
|     if isPath(fp): |     if isPath(fp): | ||||||
|         filename = fp |         filename = fp | ||||||
|  | @ -2054,7 +2054,8 @@ def open(fp, mode="r"): | ||||||
|                 #traceback.print_exc() |                 #traceback.print_exc() | ||||||
|                 pass |                 pass | ||||||
| 
 | 
 | ||||||
|     raise IOError("cannot identify image file") |     raise IOError("cannot identify image file %r" | ||||||
|  |                   % (filename if filename else fp)) | ||||||
| 
 | 
 | ||||||
| # | # | ||||||
| # Image processing. | # Image processing. | ||||||
|  |  | ||||||
|  | @ -20,15 +20,6 @@ | ||||||
| from PIL import Image | from PIL import Image | ||||||
| import re | import re | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ## |  | ||||||
| # Convert color string to RGB tuple. |  | ||||||
| # |  | ||||||
| # @param color A CSS3-style colour string. |  | ||||||
| # @return An RGB-tuple. |  | ||||||
| # @exception ValueError If the color string could not be interpreted |  | ||||||
| #    as an RGB value. |  | ||||||
| 
 |  | ||||||
| def getrgb(color): | def getrgb(color): | ||||||
|     """ |     """ | ||||||
|      Convert a color string to an RGB tuple. If the string cannot be parsed, |      Convert a color string to an RGB tuple. If the string cannot be parsed, | ||||||
|  | @ -37,7 +28,7 @@ def getrgb(color): | ||||||
|     .. versionadded:: 1.1.4 |     .. versionadded:: 1.1.4 | ||||||
| 
 | 
 | ||||||
|     :param color: A color string |     :param color: A color string | ||||||
|     :return: ``(red, green, blue)`` |     :return: ``(red, green, blue[, alpha])`` | ||||||
|     """ |     """ | ||||||
|     try: |     try: | ||||||
|         rgb = colormap[color] |         rgb = colormap[color] | ||||||
|  | @ -114,7 +105,7 @@ def getcolor(color, mode): | ||||||
|     .. versionadded:: 1.1.4 |     .. versionadded:: 1.1.4 | ||||||
| 
 | 
 | ||||||
|     :param color: A color string |     :param color: A color string | ||||||
|     :return: ``(red, green, blue)`` |     :return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])`` | ||||||
|     """ |     """ | ||||||
|     # same as getrgb, but converts the result to the given mode |     # same as getrgb, but converts the result to the given mode | ||||||
|     color, alpha = getrgb(color), 255 |     color, alpha = getrgb(color), 255 | ||||||
|  |  | ||||||
|  | @ -205,7 +205,7 @@ class ImageFile(Image.Image): | ||||||
|                         else: |                         else: | ||||||
|                             raise IndexError(ie) |                             raise IndexError(ie) | ||||||
| 
 | 
 | ||||||
|                     if not s: # truncated jpeg |                     if not s and not d.handles_eof: # truncated jpeg | ||||||
|                         self.tile = [] |                         self.tile = [] | ||||||
| 
 | 
 | ||||||
|                         # JpegDecode needs to clean things up here either way |                         # JpegDecode needs to clean things up here either way | ||||||
|  |  | ||||||
							
								
								
									
										249
									
								
								PIL/Jpeg2KImagePlugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								PIL/Jpeg2KImagePlugin.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,249 @@ | ||||||
|  | # | ||||||
|  | # The Python Imaging Library | ||||||
|  | # $Id$ | ||||||
|  | # | ||||||
|  | # JPEG2000 file handling | ||||||
|  | # | ||||||
|  | # History: | ||||||
|  | # 2014-03-12 ajh  Created | ||||||
|  | # | ||||||
|  | # Copyright (c) 2014 Coriolis Systems Limited | ||||||
|  | # Copyright (c) 2014 Alastair Houghton | ||||||
|  | # | ||||||
|  | # See the README file for information on usage and redistribution. | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | __version__ = "0.1" | ||||||
|  | 
 | ||||||
|  | from PIL import Image, ImageFile, _binary | ||||||
|  | import struct | ||||||
|  | import os | ||||||
|  | import io | ||||||
|  | 
 | ||||||
|  | def _parse_codestream(fp): | ||||||
|  |     """Parse the JPEG 2000 codestream to extract the size and component | ||||||
|  |     count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" | ||||||
|  |      | ||||||
|  |     hdr = fp.read(2) | ||||||
|  |     lsiz = struct.unpack('>H', hdr)[0] | ||||||
|  |     siz = hdr + fp.read(lsiz - 2) | ||||||
|  |     lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \ | ||||||
|  |     xtosiz, ytosiz, csiz \ | ||||||
|  |         = struct.unpack('>HHIIIIIIIIH', siz[:38]) | ||||||
|  |     ssiz = [None]*csiz | ||||||
|  |     xrsiz = [None]*csiz | ||||||
|  |     yrsiz = [None]*csiz | ||||||
|  |     for i in range(csiz): | ||||||
|  |         ssiz[i], xrsiz[i], yrsiz[i] \ | ||||||
|  |             = struct.unpack('>BBB', siz[36 + 3 * i:39 + 3 * i]) | ||||||
|  | 
 | ||||||
|  |     size = (xsiz - xosiz, ysiz - yosiz) | ||||||
|  |     if csiz == 1: | ||||||
|  |         mode = 'L' | ||||||
|  |     elif csiz == 2: | ||||||
|  |         mode = 'LA' | ||||||
|  |     elif csiz == 3: | ||||||
|  |         mode = 'RGB' | ||||||
|  |     elif csiz == 4: | ||||||
|  |         mode == 'RGBA' | ||||||
|  |     else: | ||||||
|  |         mode = None | ||||||
|  |      | ||||||
|  |     return (size, mode) | ||||||
|  | 
 | ||||||
|  | def _parse_jp2_header(fp): | ||||||
|  |     """Parse the JP2 header box to extract size, component count and | ||||||
|  |     color space information, returning a PIL (size, mode) tuple.""" | ||||||
|  |      | ||||||
|  |     # Find the JP2 header box | ||||||
|  |     header = None | ||||||
|  |     while True: | ||||||
|  |         lbox, tbox = struct.unpack('>I4s', fp.read(8)) | ||||||
|  |         if lbox == 1: | ||||||
|  |             lbox = struct.unpack('>Q', fp.read(8))[0] | ||||||
|  |             hlen = 16 | ||||||
|  |         else: | ||||||
|  |             hlen = 8 | ||||||
|  | 
 | ||||||
|  |         if tbox == b'jp2h': | ||||||
|  |             header = fp.read(lbox - hlen) | ||||||
|  |             break | ||||||
|  |         else: | ||||||
|  |             fp.seek(lbox - hlen, os.SEEK_CUR) | ||||||
|  | 
 | ||||||
|  |     if header is None: | ||||||
|  |         raise SyntaxError('could not find JP2 header') | ||||||
|  | 
 | ||||||
|  |     size = None | ||||||
|  |     mode = None | ||||||
|  |      | ||||||
|  |     hio = io.BytesIO(header) | ||||||
|  |     while True: | ||||||
|  |         lbox, tbox = struct.unpack('>I4s', hio.read(8)) | ||||||
|  |         if lbox == 1: | ||||||
|  |             lbox = struct.unpack('>Q', hio.read(8))[0] | ||||||
|  |             hlen = 16 | ||||||
|  |         else: | ||||||
|  |             hlen = 8 | ||||||
|  | 
 | ||||||
|  |         content = hio.read(lbox - hlen) | ||||||
|  | 
 | ||||||
|  |         if tbox == b'ihdr': | ||||||
|  |             height, width, nc, bpc, c, unkc, ipr \ | ||||||
|  |               = struct.unpack('>IIHBBBB', content) | ||||||
|  |             size = (width, height) | ||||||
|  |             if unkc: | ||||||
|  |                 if nc == 1: | ||||||
|  |                     mode = 'L' | ||||||
|  |                 elif nc == 2: | ||||||
|  |                     mode = 'LA' | ||||||
|  |                 elif nc == 3: | ||||||
|  |                     mode = 'RGB' | ||||||
|  |                 elif nc == 4: | ||||||
|  |                     mode = 'RGBA' | ||||||
|  |                 break | ||||||
|  |         elif tbox == b'colr': | ||||||
|  |             meth, prec, approx = struct.unpack('>BBB', content[:3]) | ||||||
|  |             if meth == 1: | ||||||
|  |                 cs = struct.unpack('>I', content[3:7])[0] | ||||||
|  |                 if cs == 16:   # sRGB | ||||||
|  |                     if nc == 3: | ||||||
|  |                         mode = 'RGB' | ||||||
|  |                     elif nc == 4: | ||||||
|  |                         mode = 'RGBA' | ||||||
|  |                     break | ||||||
|  |                 elif cs == 17: # grayscale | ||||||
|  |                     if nc == 1: | ||||||
|  |                         mode = 'L' | ||||||
|  |                     elif nc == 2: | ||||||
|  |                         mode = 'LA' | ||||||
|  |                     break | ||||||
|  |                 elif cs == 18: # sYCC | ||||||
|  |                     if nc == 3: | ||||||
|  |                         mode = 'RGB' | ||||||
|  |                     elif nc == 4: | ||||||
|  |                         mode == 'RGBA' | ||||||
|  |                     break | ||||||
|  | 
 | ||||||
|  |     return (size, mode) | ||||||
|  | 
 | ||||||
|  | ## | ||||||
|  | # Image plugin for JPEG2000 images. | ||||||
|  | 
 | ||||||
|  | class Jpeg2KImageFile(ImageFile.ImageFile): | ||||||
|  |     format = "JPEG2000" | ||||||
|  |     format_description = "JPEG 2000 (ISO 15444)" | ||||||
|  | 
 | ||||||
|  |     def _open(self): | ||||||
|  |         sig = self.fp.read(4) | ||||||
|  |         if sig == b'\xff\x4f\xff\x51': | ||||||
|  |             self.codec = "j2k" | ||||||
|  |             self.size, self.mode = _parse_codestream(self.fp) | ||||||
|  |         else: | ||||||
|  |             sig = sig + self.fp.read(8) | ||||||
|  |          | ||||||
|  |             if sig == b'\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a': | ||||||
|  |                 self.codec = "jp2" | ||||||
|  |                 self.size, self.mode = _parse_jp2_header(self.fp) | ||||||
|  |             else: | ||||||
|  |                 raise SyntaxError('not a JPEG 2000 file') | ||||||
|  |          | ||||||
|  |         if self.size is None or self.mode is None: | ||||||
|  |             raise SyntaxError('unable to determine size/mode') | ||||||
|  |          | ||||||
|  |         self.reduce = 0 | ||||||
|  |         self.layers = 0 | ||||||
|  | 
 | ||||||
|  |         fd = -1 | ||||||
|  | 
 | ||||||
|  |         if hasattr(self.fp, "fileno"): | ||||||
|  |             try: | ||||||
|  |                 fd = self.fp.fileno() | ||||||
|  |             except: | ||||||
|  |                 fd = -1 | ||||||
|  | 
 | ||||||
|  |         self.tile = [('jpeg2k', (0, 0) + self.size, 0, | ||||||
|  |                       (self.codec, self.reduce, self.layers, fd))] | ||||||
|  | 
 | ||||||
|  |     def load(self): | ||||||
|  |         if self.reduce: | ||||||
|  |             power = 1 << self.reduce | ||||||
|  |             adjust = power >> 1 | ||||||
|  |             self.size = (int((self.size[0] + adjust) / power), | ||||||
|  |                          int((self.size[1] + adjust) / power)) | ||||||
|  | 
 | ||||||
|  |         if self.tile: | ||||||
|  |             # Update the reduce and layers settings | ||||||
|  |             t = self.tile[0] | ||||||
|  |             t3 = (t[3][0], self.reduce, self.layers, t[3][3]) | ||||||
|  |             self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] | ||||||
|  |          | ||||||
|  |         ImageFile.ImageFile.load(self) | ||||||
|  |          | ||||||
|  | def _accept(prefix): | ||||||
|  |     return (prefix[:4] == b'\xff\x4f\xff\x51' | ||||||
|  |             or prefix[:12] == b'\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a') | ||||||
|  | 
 | ||||||
|  | # ------------------------------------------------------------ | ||||||
|  | # Save support | ||||||
|  | 
 | ||||||
|  | def _save(im, fp, filename): | ||||||
|  |     if filename.endswith('.j2k'): | ||||||
|  |         kind = 'j2k' | ||||||
|  |     else: | ||||||
|  |         kind = 'jp2' | ||||||
|  | 
 | ||||||
|  |     # Get the keyword arguments | ||||||
|  |     info = im.encoderinfo | ||||||
|  | 
 | ||||||
|  |     offset = info.get('offset', None) | ||||||
|  |     tile_offset = info.get('tile_offset', None) | ||||||
|  |     tile_size = info.get('tile_size', None) | ||||||
|  |     quality_mode = info.get('quality_mode', 'rates') | ||||||
|  |     quality_layers = info.get('quality_layers', None) | ||||||
|  |     num_resolutions = info.get('num_resolutions', 0) | ||||||
|  |     cblk_size = info.get('codeblock_size', None) | ||||||
|  |     precinct_size = info.get('precinct_size', None) | ||||||
|  |     irreversible = info.get('irreversible', False) | ||||||
|  |     progression = info.get('progression', 'LRCP') | ||||||
|  |     cinema_mode = info.get('cinema_mode', 'no') | ||||||
|  |     fd = -1 | ||||||
|  | 
 | ||||||
|  |     if hasattr(fp, "fileno"): | ||||||
|  |         try: | ||||||
|  |             fd = fp.fileno() | ||||||
|  |         except: | ||||||
|  |             fd = -1 | ||||||
|  |      | ||||||
|  |     im.encoderconfig = ( | ||||||
|  |         offset, | ||||||
|  |         tile_offset, | ||||||
|  |         tile_size, | ||||||
|  |         quality_mode, | ||||||
|  |         quality_layers, | ||||||
|  |         num_resolutions, | ||||||
|  |         cblk_size, | ||||||
|  |         precinct_size, | ||||||
|  |         irreversible, | ||||||
|  |         progression, | ||||||
|  |         cinema_mode, | ||||||
|  |         fd | ||||||
|  |         ) | ||||||
|  |          | ||||||
|  |     ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)]) | ||||||
|  |      | ||||||
|  | # ------------------------------------------------------------ | ||||||
|  | # Registry stuff | ||||||
|  | 
 | ||||||
|  | Image.register_open('JPEG2000', Jpeg2KImageFile, _accept) | ||||||
|  | Image.register_save('JPEG2000', _save) | ||||||
|  | 
 | ||||||
|  | Image.register_extension('JPEG2000', '.jp2') | ||||||
|  | Image.register_extension('JPEG2000', '.j2k') | ||||||
|  | Image.register_extension('JPEG2000', '.jpc') | ||||||
|  | Image.register_extension('JPEG2000', '.jpf') | ||||||
|  | Image.register_extension('JPEG2000', '.jpx') | ||||||
|  | Image.register_extension('JPEG2000', '.j2c') | ||||||
|  | 
 | ||||||
|  | Image.register_mime('JPEG2000', 'image/jp2') | ||||||
|  | Image.register_mime('JPEG2000', 'image/jpx') | ||||||
|  | @ -442,7 +442,7 @@ samplings = { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| def convert_dict_qtables(qtables): | def convert_dict_qtables(qtables): | ||||||
|     qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)] |     qtables = [qtables[key] for key in range(len(qtables)) if key in qtables] | ||||||
|     for idx, table in enumerate(qtables): |     for idx, table in enumerate(qtables): | ||||||
|         qtables[idx] = [table[i] for i in zigzag_index] |         qtables[idx] = [table[i] for i in zigzag_index] | ||||||
|     return qtables |     return qtables | ||||||
|  | @ -504,7 +504,7 @@ def _save(im, fp, filename): | ||||||
|             except ValueError: |             except ValueError: | ||||||
|                 raise ValueError("Invalid quantization table") |                 raise ValueError("Invalid quantization table") | ||||||
|             else: |             else: | ||||||
|                 qtables = [lines[s:s+64] for s in xrange(0, len(lines), 64)] |                 qtables = [lines[s:s+64] for s in range(0, len(lines), 64)] | ||||||
|         if isinstance(qtables, (tuple, list, dict)): |         if isinstance(qtables, (tuple, list, dict)): | ||||||
|             if isinstance(qtables, dict): |             if isinstance(qtables, dict): | ||||||
|                 qtables = convert_dict_qtables(qtables) |                 qtables = convert_dict_qtables(qtables) | ||||||
|  |  | ||||||
|  | @ -693,10 +693,11 @@ class TiffImageFile(ImageFile.ImageFile): | ||||||
|         if not len(self.tile) == 1: |         if not len(self.tile) == 1: | ||||||
|             raise IOError("Not exactly one tile") |             raise IOError("Not exactly one tile") | ||||||
| 
 | 
 | ||||||
|         d, e, o, a = self.tile[0] |         # (self._compression, (extents tuple), 0, (rawmode, self._compression, fp)) | ||||||
|         d = Image._getdecoder(self.mode, 'libtiff', a, self.decoderconfig) |         ignored, extents, ignored_2, args = self.tile[0] | ||||||
|  |         decoder = Image._getdecoder(self.mode, 'libtiff', args, self.decoderconfig) | ||||||
|         try: |         try: | ||||||
|             d.setimage(self.im, e) |             decoder.setimage(self.im, extents) | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             raise IOError("Couldn't set the image") |             raise IOError("Couldn't set the image") | ||||||
| 
 | 
 | ||||||
|  | @ -712,27 +713,30 @@ class TiffImageFile(ImageFile.ImageFile): | ||||||
|             # with here by reordering. |             # with here by reordering. | ||||||
|             if Image.DEBUG: |             if Image.DEBUG: | ||||||
|                 print ("have getvalue. just sending in a string from getvalue") |                 print ("have getvalue. just sending in a string from getvalue") | ||||||
|             n,e = d.decode(self.fp.getvalue()) |             n,err = decoder.decode(self.fp.getvalue()) | ||||||
|         elif hasattr(self.fp, "fileno"): |         elif hasattr(self.fp, "fileno"): | ||||||
|             # we've got a actual file on disk, pass in the fp. |             # we've got a actual file on disk, pass in the fp. | ||||||
|             if Image.DEBUG: |             if Image.DEBUG: | ||||||
|                 print ("have fileno, calling fileno version of the decoder.") |                 print ("have fileno, calling fileno version of the decoder.") | ||||||
|             self.fp.seek(0) |             self.fp.seek(0) | ||||||
|             n,e = d.decode(b"fpfp") # 4 bytes, otherwise the trace might error out |             n,err = decoder.decode(b"fpfp") # 4 bytes, otherwise the trace might error out | ||||||
|         else: |         else: | ||||||
|             # we have something else. |             # we have something else. | ||||||
|             if Image.DEBUG: |             if Image.DEBUG: | ||||||
|                 print ("don't have fileno or getvalue. just reading") |                 print ("don't have fileno or getvalue. just reading") | ||||||
|             # UNDONE -- so much for that buffer size thing. |             # UNDONE -- so much for that buffer size thing. | ||||||
|             n, e = d.decode(self.fp.read()) |             n,err = decoder.decode(self.fp.read()) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         self.tile = [] |         self.tile = [] | ||||||
|         self.readonly = 0 |         self.readonly = 0 | ||||||
|  |         # libtiff closed the fp in a, we need to close self.fp, if possible | ||||||
|  |         if hasattr(self.fp, 'close'): | ||||||
|  |             self.fp.close() | ||||||
|         self.fp = None # might be shared |         self.fp = None # might be shared | ||||||
| 
 | 
 | ||||||
|         if e < 0: |         if err < 0: | ||||||
|             raise IOError(e) |             raise IOError(err) | ||||||
| 
 | 
 | ||||||
|         self.load_end() |         self.load_end() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ _plugins = ['ArgImagePlugin', | ||||||
|             'ImtImagePlugin', |             'ImtImagePlugin', | ||||||
|             'IptcImagePlugin', |             'IptcImagePlugin', | ||||||
|             'JpegImagePlugin', |             'JpegImagePlugin', | ||||||
|  |             'Jpeg2KImagePlugin', | ||||||
|             'McIdasImagePlugin', |             'McIdasImagePlugin', | ||||||
|             'MicImagePlugin', |             'MicImagePlugin', | ||||||
|             'MpegImagePlugin', |             'MpegImagePlugin', | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								Tests/images/pillow2.icns
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/pillow2.icns
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/pillow3.icns
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/pillow3.icns
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/test-card-lossless.jp2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/test-card-lossless.jp2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/test-card-lossy-tiled.jp2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/test-card-lossy-tiled.jp2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/test-card.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/test-card.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 30 KiB | 
							
								
								
									
										29
									
								
								Tests/run.py
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								Tests/run.py
									
									
									
									
									
								
							|  | @ -2,7 +2,7 @@ from __future__ import print_function | ||||||
| 
 | 
 | ||||||
| # minimal test runner | # minimal test runner | ||||||
| 
 | 
 | ||||||
| import glob, os, os.path, sys, tempfile | import glob, os, os.path, sys, tempfile, re | ||||||
| 
 | 
 | ||||||
| try: | try: | ||||||
|     root = os.path.dirname(__file__) |     root = os.path.dirname(__file__) | ||||||
|  | @ -38,6 +38,8 @@ skipped = [] | ||||||
| python_options = " ".join(python_options) | python_options = " ".join(python_options) | ||||||
| tester_options = " ".join(tester_options) | tester_options = " ".join(tester_options) | ||||||
| 
 | 
 | ||||||
|  | ignore_re = re.compile('^ignore: (.*)$', re.MULTILINE) | ||||||
|  | 
 | ||||||
| for file in files: | for file in files: | ||||||
|     test, ext = os.path.splitext(os.path.basename(file)) |     test, ext = os.path.splitext(os.path.basename(file)) | ||||||
|     if include and test not in include: |     if include and test not in include: | ||||||
|  | @ -48,7 +50,30 @@ for file in files: | ||||||
|     out = os.popen("%s %s -u %s %s 2>&1" % ( |     out = os.popen("%s %s -u %s %s 2>&1" % ( | ||||||
|             sys.executable, python_options, file, tester_options |             sys.executable, python_options, file, tester_options | ||||||
|             )) |             )) | ||||||
|     result = out.read().strip() |     result = out.read() | ||||||
|  | 
 | ||||||
|  |     # Extract any ignore patterns | ||||||
|  |     ignore_pats = ignore_re.findall(result) | ||||||
|  |     result = ignore_re.sub('', result) | ||||||
|  | 
 | ||||||
|  |     try: | ||||||
|  |         def fix_re(p): | ||||||
|  |             if not p.startswith('^'): | ||||||
|  |                 p = '^' + p | ||||||
|  |             if not p.endswith('$'): | ||||||
|  |                 p = p + '$' | ||||||
|  |             return p | ||||||
|  |          | ||||||
|  |         ignore_res = [re.compile(fix_re(p), re.MULTILINE) for p in ignore_pats] | ||||||
|  |     except: | ||||||
|  |         print('(bad ignore patterns %r)' % ignore_pats) | ||||||
|  |         ignore_res = [] | ||||||
|  | 
 | ||||||
|  |     for r in ignore_res: | ||||||
|  |         result = r.sub('', result) | ||||||
|  | 
 | ||||||
|  |     result = result.strip() | ||||||
|  |      | ||||||
|     if result == "ok": |     if result == "ok": | ||||||
|         result = None |         result = None | ||||||
|     elif result == "skip": |     elif result == "skip": | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from tester import * | ||||||
| from PIL import Image | from PIL import Image | ||||||
| import os | import os | ||||||
| 
 | 
 | ||||||
| base = 'Tests/images/bmp/' | base = os.path.join('Tests', 'images', 'bmp') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_files(d, ext='.bmp'): | def get_files(d, ext='.bmp'): | ||||||
|  | @ -78,9 +78,9 @@ def test_good(): | ||||||
|              |              | ||||||
|         except Exception as msg: |         except Exception as msg: | ||||||
|             # there are three here that are unsupported: |             # there are three here that are unsupported: | ||||||
|             unsupported = ('Tests/images/bmp/g/rgb32bf.bmp', |             unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), | ||||||
|                            'Tests/images/bmp/g/pal8rle.bmp', |                            os.path.join(base, 'g', 'pal8rle.bmp'), | ||||||
|                            'Tests/images/bmp/g/pal4rle.bmp') |                            os.path.join(base, 'g', 'pal4rle.bmp')) | ||||||
|             if f not in unsupported: |             if f not in unsupported: | ||||||
|                 assert_true(False, "Unsupported Image %s: %s" %(f,msg)) |                 assert_true(False, "Unsupported Image %s: %s" %(f,msg)) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										66
									
								
								Tests/test_file_icns.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								Tests/test_file_icns.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | ||||||
|  | from tester import * | ||||||
|  | 
 | ||||||
|  | from PIL import Image | ||||||
|  | 
 | ||||||
|  | # sample icon file | ||||||
|  | file = "Images/pillow.icns" | ||||||
|  | data = open(file, "rb").read() | ||||||
|  | 
 | ||||||
|  | enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') | ||||||
|  | 
 | ||||||
|  | def test_sanity(): | ||||||
|  |     # Loading this icon by default should result in the largest size | ||||||
|  |     # (512x512@2x) being loaded | ||||||
|  |     im = Image.open(file) | ||||||
|  |     im.load() | ||||||
|  |     assert_equal(im.mode, "RGBA") | ||||||
|  |     assert_equal(im.size, (1024, 1024)) | ||||||
|  |     assert_equal(im.format, "ICNS") | ||||||
|  | 
 | ||||||
|  | def test_sizes(): | ||||||
|  |     # Check that we can load all of the sizes, and that the final pixel | ||||||
|  |     # dimensions are as expected | ||||||
|  |     im = Image.open(file) | ||||||
|  |     for w,h,r in im.info['sizes']: | ||||||
|  |         wr = w * r | ||||||
|  |         hr = h * r | ||||||
|  |         im2 = Image.open(file) | ||||||
|  |         im2.size = (w, h, r) | ||||||
|  |         im2.load() | ||||||
|  |         assert_equal(im2.mode, 'RGBA') | ||||||
|  |         assert_equal(im2.size, (wr, hr)) | ||||||
|  | 
 | ||||||
|  | def test_older_icon(): | ||||||
|  |     # This icon was made with Icon Composer rather than iconutil; it still | ||||||
|  |     # uses PNG rather than JP2, however (since it was made on 10.9). | ||||||
|  |     im = Image.open('Tests/images/pillow2.icns') | ||||||
|  |     for w,h,r in im.info['sizes']: | ||||||
|  |         wr = w * r | ||||||
|  |         hr = h * r | ||||||
|  |         im2 = Image.open('Tests/images/pillow2.icns') | ||||||
|  |         im2.size = (w, h, r) | ||||||
|  |         im2.load() | ||||||
|  |         assert_equal(im2.mode, 'RGBA') | ||||||
|  |         assert_equal(im2.size, (wr, hr)) | ||||||
|  | 
 | ||||||
|  | def test_jp2_icon(): | ||||||
|  |     # This icon was made by using Uli Kusterer's oldiconutil to replace | ||||||
|  |     # the PNG images with JPEG 2000 ones.  The advantage of doing this is | ||||||
|  |     # that OS X 10.5 supports JPEG 2000 but not PNG; some commercial | ||||||
|  |     # software therefore does just this. | ||||||
|  |      | ||||||
|  |     # (oldiconutil is here: https://github.com/uliwitness/oldiconutil) | ||||||
|  | 
 | ||||||
|  |     if not enable_jpeg2k: | ||||||
|  |         return | ||||||
|  |      | ||||||
|  |     im = Image.open('Tests/images/pillow3.icns') | ||||||
|  |     for w,h,r in im.info['sizes']: | ||||||
|  |         wr = w * r | ||||||
|  |         hr = h * r | ||||||
|  |         im2 = Image.open('Tests/images/pillow3.icns') | ||||||
|  |         im2.size = (w, h, r) | ||||||
|  |         im2.load() | ||||||
|  |         assert_equal(im2.mode, 'RGBA') | ||||||
|  |         assert_equal(im2.size, (wr, hr)) | ||||||
|  |      | ||||||
|  | @ -10,9 +10,7 @@ codecs = dir(Image.core) | ||||||
| if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: | if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: | ||||||
|     skip("jpeg support not available") |     skip("jpeg support not available") | ||||||
| 
 | 
 | ||||||
| # sample jpeg stream | test_file = "Images/lena.jpg" | ||||||
| file = "Images/lena.jpg" |  | ||||||
| data = open(file, "rb").read() |  | ||||||
| 
 | 
 | ||||||
| def roundtrip(im, **options): | def roundtrip(im, **options): | ||||||
|     out = BytesIO() |     out = BytesIO() | ||||||
|  | @ -30,7 +28,7 @@ def test_sanity(): | ||||||
|     # internal version number |     # internal version number | ||||||
|     assert_match(Image.core.jpeglib_version, "\d+\.\d+$") |     assert_match(Image.core.jpeglib_version, "\d+\.\d+$") | ||||||
| 
 | 
 | ||||||
|     im = Image.open(file) |     im = Image.open(test_file) | ||||||
|     im.load() |     im.load() | ||||||
|     assert_equal(im.mode, "RGB") |     assert_equal(im.mode, "RGB") | ||||||
|     assert_equal(im.size, (128, 128)) |     assert_equal(im.size, (128, 128)) | ||||||
|  | @ -40,7 +38,7 @@ def test_sanity(): | ||||||
| 
 | 
 | ||||||
| def test_app(): | def test_app(): | ||||||
|     # Test APP/COM reader (@PIL135) |     # Test APP/COM reader (@PIL135) | ||||||
|     im = Image.open(file) |     im = Image.open(test_file) | ||||||
|     assert_equal(im.applist[0], |     assert_equal(im.applist[0], | ||||||
|                  ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00")) |                  ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00")) | ||||||
|     assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) |     assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) | ||||||
|  | @ -49,8 +47,8 @@ def test_app(): | ||||||
| def test_cmyk(): | def test_cmyk(): | ||||||
|     # Test CMYK handling.  Thanks to Tim and Charlie for test data, |     # Test CMYK handling.  Thanks to Tim and Charlie for test data, | ||||||
|     # Michael for getting me to look one more time. |     # Michael for getting me to look one more time. | ||||||
|     file = "Tests/images/pil_sample_cmyk.jpg" |     f = "Tests/images/pil_sample_cmyk.jpg" | ||||||
|     im = Image.open(file) |     im = Image.open(f) | ||||||
|     # the source image has red pixels in the upper left corner. |     # the source image has red pixels in the upper left corner. | ||||||
|     c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] |     c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] | ||||||
|     assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) |     assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) | ||||||
|  | @ -66,7 +64,7 @@ def test_cmyk(): | ||||||
| 
 | 
 | ||||||
| def test_dpi(): | def test_dpi(): | ||||||
|     def test(xdpi, ydpi=None): |     def test(xdpi, ydpi=None): | ||||||
|         im = Image.open(file) |         im = Image.open(test_file) | ||||||
|         im = roundtrip(im, dpi=(xdpi, ydpi or xdpi)) |         im = roundtrip(im, dpi=(xdpi, ydpi or xdpi)) | ||||||
|         return im.info.get("dpi") |         return im.info.get("dpi") | ||||||
|     assert_equal(test(72), (72, 72)) |     assert_equal(test(72), (72, 72)) | ||||||
|  | @ -80,9 +78,9 @@ def test_icc(): | ||||||
|     icc_profile = im1.info["icc_profile"] |     icc_profile = im1.info["icc_profile"] | ||||||
|     assert_equal(len(icc_profile), 3144) |     assert_equal(len(icc_profile), 3144) | ||||||
|     # Roundtrip via physical file. |     # Roundtrip via physical file. | ||||||
|     file = tempfile("temp.jpg") |     f = tempfile("temp.jpg") | ||||||
|     im1.save(file, icc_profile=icc_profile) |     im1.save(f, icc_profile=icc_profile) | ||||||
|     im2 = Image.open(file) |     im2 = Image.open(f) | ||||||
|     assert_equal(im2.info.get("icc_profile"), icc_profile) |     assert_equal(im2.info.get("icc_profile"), icc_profile) | ||||||
|     # Roundtrip via memory buffer. |     # Roundtrip via memory buffer. | ||||||
|     im1 = roundtrip(lena()) |     im1 = roundtrip(lena()) | ||||||
|  | @ -203,3 +201,9 @@ def test_exif(): | ||||||
|     im = Image.open("Tests/images/pil_sample_rgb.jpg") |     im = Image.open("Tests/images/pil_sample_rgb.jpg") | ||||||
|     info = im._getexif() |     info = im._getexif() | ||||||
|     assert_equal(info[305], 'Adobe Photoshop CS Macintosh') |     assert_equal(info[305], 'Adobe Photoshop CS Macintosh') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_quality_keep(): | ||||||
|  |     im = Image.open("Images/lena.jpg") | ||||||
|  |     f = tempfile('temp.jpg') | ||||||
|  |     assert_no_exception(lambda: im.save(f, quality='keep')) | ||||||
|  |  | ||||||
							
								
								
									
										110
									
								
								Tests/test_file_jpeg2k.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								Tests/test_file_jpeg2k.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | ||||||
|  | from tester import * | ||||||
|  | 
 | ||||||
|  | from PIL import Image | ||||||
|  | from PIL import ImageFile | ||||||
|  | 
 | ||||||
|  | codecs = dir(Image.core) | ||||||
|  | 
 | ||||||
|  | if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs: | ||||||
|  |     skip('JPEG 2000 support not available') | ||||||
|  | 
 | ||||||
|  | # OpenJPEG 2.0.0 outputs this debugging message sometimes; we should | ||||||
|  | # ignore it---it doesn't represent a test failure. | ||||||
|  | ignore('Not enough memory to handle tile data') | ||||||
|  | 
 | ||||||
|  | test_card = Image.open('Tests/images/test-card.png') | ||||||
|  | test_card.load() | ||||||
|  | 
 | ||||||
|  | def roundtrip(im, **options): | ||||||
|  |     out = BytesIO() | ||||||
|  |     im.save(out, "JPEG2000", **options) | ||||||
|  |     bytes = out.tell() | ||||||
|  |     out.seek(0) | ||||||
|  |     im = Image.open(out) | ||||||
|  |     im.bytes = bytes # for testing only | ||||||
|  |     im.load() | ||||||
|  |     return im | ||||||
|  | 
 | ||||||
|  | # ---------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | def test_sanity(): | ||||||
|  |     # Internal version number | ||||||
|  |     assert_match(Image.core.jp2klib_version, '\d+\.\d+\.\d+$') | ||||||
|  | 
 | ||||||
|  |     im = Image.open('Tests/images/test-card-lossless.jp2') | ||||||
|  |     im.load() | ||||||
|  |     assert_equal(im.mode, 'RGB') | ||||||
|  |     assert_equal(im.size, (640, 480)) | ||||||
|  |     assert_equal(im.format, 'JPEG2000') | ||||||
|  |      | ||||||
|  | # ---------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | # These two test pre-written JPEG 2000 files that were not written with | ||||||
|  | # PIL (they were made using Adobe Photoshop) | ||||||
|  | 
 | ||||||
|  | def test_lossless(): | ||||||
|  |     im = Image.open('Tests/images/test-card-lossless.jp2') | ||||||
|  |     im.load() | ||||||
|  |     im.save('/tmp/test-card.png') | ||||||
|  |     assert_image_similar(im, test_card, 1.0e-3) | ||||||
|  | 
 | ||||||
|  | def test_lossy_tiled(): | ||||||
|  |     im = Image.open('Tests/images/test-card-lossy-tiled.jp2') | ||||||
|  |     im.load() | ||||||
|  |     assert_image_similar(im, test_card, 2.0) | ||||||
|  | 
 | ||||||
|  | # ---------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | def test_lossless_rt(): | ||||||
|  |     im = roundtrip(test_card) | ||||||
|  |     assert_image_equal(im, test_card) | ||||||
|  | 
 | ||||||
|  | def test_lossy_rt(): | ||||||
|  |     im = roundtrip(test_card, quality_layers=[20]) | ||||||
|  |     assert_image_similar(im, test_card, 2.0) | ||||||
|  | 
 | ||||||
|  | def test_tiled_rt(): | ||||||
|  |     im = roundtrip(test_card, tile_size=(128, 128)) | ||||||
|  |     assert_image_equal(im, test_card) | ||||||
|  | 
 | ||||||
|  | def test_tiled_offset_rt(): | ||||||
|  |     im = roundtrip(test_card, tile_size=(128, 128), tile_offset=(0, 0), | ||||||
|  |                    offset=(32, 32)) | ||||||
|  |     assert_image_equal(im, test_card) | ||||||
|  |      | ||||||
|  | def test_irreversible_rt(): | ||||||
|  |     im = roundtrip(test_card, irreversible=True, quality_layers=[20]) | ||||||
|  |     assert_image_similar(im, test_card, 2.0) | ||||||
|  | 
 | ||||||
|  | def test_prog_qual_rt(): | ||||||
|  |     im = roundtrip(test_card, quality_layers=[60, 40, 20], progression='LRCP') | ||||||
|  |     assert_image_similar(im, test_card, 2.0) | ||||||
|  | 
 | ||||||
|  | def test_prog_res_rt(): | ||||||
|  |     im = roundtrip(test_card, num_resolutions=8, progression='RLCP') | ||||||
|  |     assert_image_equal(im, test_card) | ||||||
|  | 
 | ||||||
|  | # ---------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | def test_reduce(): | ||||||
|  |     im = Image.open('Tests/images/test-card-lossless.jp2') | ||||||
|  |     im.reduce = 2 | ||||||
|  |     im.load() | ||||||
|  |     assert_equal(im.size, (160, 120)) | ||||||
|  | 
 | ||||||
|  | def test_layers(): | ||||||
|  |     out = BytesIO() | ||||||
|  |     test_card.save(out, 'JPEG2000', quality_layers=[100, 50, 10], | ||||||
|  |                    progression='LRCP') | ||||||
|  |     out.seek(0) | ||||||
|  |      | ||||||
|  |     im = Image.open(out) | ||||||
|  |     im.layers = 1 | ||||||
|  |     im.load() | ||||||
|  |     assert_image_similar(im, test_card, 13) | ||||||
|  | 
 | ||||||
|  |     out.seek(0) | ||||||
|  |     im = Image.open(out) | ||||||
|  |     im.layers = 3 | ||||||
|  |     im.load() | ||||||
|  |     assert_image_similar(im, test_card, 0.4) | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| from tester import * | from tester import * | ||||||
|  | import os | ||||||
| 
 | 
 | ||||||
| from PIL import Image, TiffImagePlugin | from PIL import Image, TiffImagePlugin | ||||||
| 
 | 
 | ||||||
|  | @ -295,3 +296,13 @@ def xtest_bw_compression_wRGB(): | ||||||
|     assert_exception(IOError, lambda: im.save(out, compression='group3')) |     assert_exception(IOError, lambda: im.save(out, compression='group3')) | ||||||
|     assert_exception(IOError, lambda: im.save(out, compression='group4')) |     assert_exception(IOError, lambda: im.save(out, compression='group4')) | ||||||
| 
 | 
 | ||||||
|  | def test_fp_leak(): | ||||||
|  |     im = Image.open("Tests/images/lena_g4_500.tif") | ||||||
|  |     fn = im.fp.fileno() | ||||||
|  | 
 | ||||||
|  |     assert_no_exception(lambda: os.fstat(fn)) | ||||||
|  |     im.load()  # this should close it.  | ||||||
|  |     assert_exception(OSError, lambda: os.fstat(fn))  | ||||||
|  |     im = None  # this should force even more closed. | ||||||
|  |     assert_exception(OSError, lambda: os.fstat(fn))  | ||||||
|  |     assert_exception(OSError, lambda: os.close(fn)) | ||||||
|  |  | ||||||
|  | @ -2,6 +2,11 @@ from tester import * | ||||||
| 
 | 
 | ||||||
| from PIL import Image | from PIL import Image | ||||||
| 
 | 
 | ||||||
|  | if hasattr(sys, 'pypy_version_info'): | ||||||
|  |     # This takes _forever_ on pypy. Open Bug, | ||||||
|  |     # see https://github.com/python-imaging/Pillow/issues/484 | ||||||
|  |     skip() | ||||||
|  | 
 | ||||||
| def test_sanity(): | def test_sanity(): | ||||||
| 
 | 
 | ||||||
|     im = lena() |     im = lena() | ||||||
|  |  | ||||||
|  | @ -41,7 +41,11 @@ Image.new("L", (1, 1), "white") | ||||||
| 
 | 
 | ||||||
| assert_equal(0, ImageColor.getcolor("black", "1")) | assert_equal(0, ImageColor.getcolor("black", "1")) | ||||||
| assert_equal(255, ImageColor.getcolor("white", "1")) | assert_equal(255, ImageColor.getcolor("white", "1")) | ||||||
|  | # The following test is wrong, but is current behavior | ||||||
|  | # The correct result should be 255 due to the mode 1  | ||||||
| assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) | assert_equal(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) | ||||||
|  | # Correct behavior | ||||||
|  | # assert_equal(255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) | ||||||
| Image.new("1", (1, 1), "white") | Image.new("1", (1, 1), "white") | ||||||
| 
 | 
 | ||||||
| assert_equal((0, 255), ImageColor.getcolor("black", "LA")) | assert_equal((0, 255), ImageColor.getcolor("black", "LA")) | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ from tester import * | ||||||
| from PIL import Image | from PIL import Image | ||||||
| try: | try: | ||||||
|     from PIL import ImageTk |     from PIL import ImageTk | ||||||
| except ImportError as v: | except (OSError, ImportError) as v: | ||||||
|     skip(v) |     skip(v) | ||||||
| 
 | 
 | ||||||
| success() | success() | ||||||
|  |  | ||||||
|  | @ -260,6 +260,11 @@ def skip(msg=None): | ||||||
|     print("skip") |     print("skip") | ||||||
|     os._exit(0) # don't run exit handlers |     os._exit(0) # don't run exit handlers | ||||||
| 
 | 
 | ||||||
|  | def ignore(pattern): | ||||||
|  |     """Tells the driver to ignore messages matching the pattern, for the | ||||||
|  |     duration of the current test.""" | ||||||
|  |     print('ignore: %s' % pattern) | ||||||
|  | 
 | ||||||
| def _setup(): | def _setup(): | ||||||
|     global _logfile |     global _logfile | ||||||
|     def report(): |     def report(): | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								_imaging.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								_imaging.c
									
									
									
									
									
								
							|  | @ -3325,6 +3325,7 @@ extern PyObject* PyImaging_FliDecoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args); | ||||||
|  | extern PyObject* PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args); | ||||||
|  | @ -3341,6 +3342,7 @@ extern PyObject* PyImaging_ZipDecoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_JpegEncoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_JpegEncoderNew(PyObject* self, PyObject* args); | ||||||
|  | extern PyObject* PyImaging_Jpeg2KEncoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args); | ||||||
| extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args); | extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args); | ||||||
|  | @ -3393,6 +3395,10 @@ static PyMethodDef functions[] = { | ||||||
| #ifdef HAVE_LIBJPEG | #ifdef HAVE_LIBJPEG | ||||||
|     {"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1}, |     {"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1}, | ||||||
|     {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1}, |     {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1}, | ||||||
|  | #endif | ||||||
|  | #ifdef HAVE_OPENJPEG | ||||||
|  |     {"jpeg2k_decoder", (PyCFunction)PyImaging_Jpeg2KDecoderNew, 1}, | ||||||
|  |     {"jpeg2k_encoder", (PyCFunction)PyImaging_Jpeg2KEncoderNew, 1}, | ||||||
| #endif | #endif | ||||||
|     {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, |     {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, | ||||||
| #ifdef HAVE_LIBTIFF | #ifdef HAVE_LIBTIFF | ||||||
|  | @ -3497,6 +3503,13 @@ setup_module(PyObject* m) { | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef HAVE_OPENJPEG | ||||||
|  |   { | ||||||
|  |     extern const char *ImagingJpeg2KVersion(void); | ||||||
|  |     PyDict_SetItemString(d, "jp2klib_version", PyUnicode_FromString(ImagingJpeg2KVersion())); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef HAVE_LIBZ | #ifdef HAVE_LIBZ | ||||||
|   /* zip encoding strategies */ |   /* zip encoding strategies */ | ||||||
|   PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY); |   PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY); | ||||||
|  |  | ||||||
							
								
								
									
										71
									
								
								decode.c
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								decode.c
									
									
									
									
									
								
							|  | @ -52,6 +52,7 @@ typedef struct { | ||||||
|     struct ImagingCodecStateInstance state; |     struct ImagingCodecStateInstance state; | ||||||
|     Imaging im; |     Imaging im; | ||||||
|     PyObject* lock; |     PyObject* lock; | ||||||
|  |     int     handles_eof; | ||||||
| } ImagingDecoderObject; | } ImagingDecoderObject; | ||||||
| 
 | 
 | ||||||
| static PyTypeObject ImagingDecoderType; | static PyTypeObject ImagingDecoderType; | ||||||
|  | @ -93,6 +94,9 @@ PyImaging_DecoderNew(int contextsize) | ||||||
|     /* Initialize the cleanup function pointer */ |     /* Initialize the cleanup function pointer */ | ||||||
|     decoder->cleanup = NULL; |     decoder->cleanup = NULL; | ||||||
| 
 | 
 | ||||||
|  |     /* Most decoders don't want to handle EOF themselves */ | ||||||
|  |     decoder->handles_eof = 0; | ||||||
|  | 
 | ||||||
|     return decoder; |     return decoder; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -194,6 +198,12 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) | ||||||
|     return Py_None; |     return Py_None; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static PyObject * | ||||||
|  | _get_handles_eof(ImagingDecoderObject *decoder) | ||||||
|  | { | ||||||
|  |     return PyBool_FromLong(decoder->handles_eof); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static struct PyMethodDef methods[] = { | static struct PyMethodDef methods[] = { | ||||||
|     {"decode", (PyCFunction)_decode, 1}, |     {"decode", (PyCFunction)_decode, 1}, | ||||||
|     {"cleanup", (PyCFunction)_decode_cleanup, 1}, |     {"cleanup", (PyCFunction)_decode_cleanup, 1}, | ||||||
|  | @ -201,6 +211,13 @@ static struct PyMethodDef methods[] = { | ||||||
|     {NULL, NULL} /* sentinel */ |     {NULL, NULL} /* sentinel */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static struct PyGetSetDef getseters[] = { | ||||||
|  |     {"handles_eof", (getter)_get_handles_eof, NULL, | ||||||
|  |      "True if this decoder expects to handle EOF itself.", | ||||||
|  |      NULL}, | ||||||
|  |     {NULL, NULL, NULL, NULL, NULL} /* sentinel */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static PyTypeObject ImagingDecoderType = { | static PyTypeObject ImagingDecoderType = { | ||||||
|     PyVarObject_HEAD_INIT(NULL, 0) |     PyVarObject_HEAD_INIT(NULL, 0) | ||||||
|     "ImagingDecoder",               /*tp_name*/ |     "ImagingDecoder",               /*tp_name*/ | ||||||
|  | @ -232,7 +249,7 @@ static PyTypeObject ImagingDecoderType = { | ||||||
|     0,                          /*tp_iternext*/ |     0,                          /*tp_iternext*/ | ||||||
|     methods,                    /*tp_methods*/ |     methods,                    /*tp_methods*/ | ||||||
|     0,                          /*tp_members*/ |     0,                          /*tp_members*/ | ||||||
|     0,                          /*tp_getset*/ |     getseters,                  /*tp_getset*/ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||||
|  | @ -762,3 +779,55 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) | ||||||
|     return (PyObject*) decoder; |     return (PyObject*) decoder; | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* JPEG 2000                                                            */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | #ifdef HAVE_OPENJPEG | ||||||
|  | 
 | ||||||
|  | #include "Jpeg2K.h" | ||||||
|  | 
 | ||||||
|  | PyObject* | ||||||
|  | PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) | ||||||
|  | { | ||||||
|  |     ImagingDecoderObject* decoder; | ||||||
|  |     JPEG2KDECODESTATE *context; | ||||||
|  | 
 | ||||||
|  |     char* mode; | ||||||
|  |     char* format; | ||||||
|  |     OPJ_CODEC_FORMAT codec_format; | ||||||
|  |     int reduce = 0; | ||||||
|  |     int layers = 0; | ||||||
|  |     int fd = -1; | ||||||
|  |     if (!PyArg_ParseTuple(args, "ss|iii", &mode, &format, | ||||||
|  |                           &reduce, &layers, &fd)) | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     if (strcmp(format, "j2k") == 0) | ||||||
|  |         codec_format = OPJ_CODEC_J2K; | ||||||
|  |     else if (strcmp(format, "jpt") == 0) | ||||||
|  |         codec_format = OPJ_CODEC_JPT; | ||||||
|  |     else if (strcmp(format, "jp2") == 0) | ||||||
|  |         codec_format = OPJ_CODEC_JP2; | ||||||
|  |     else | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     decoder = PyImaging_DecoderNew(sizeof(JPEG2KDECODESTATE)); | ||||||
|  |     if (decoder == NULL) | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     decoder->handles_eof = 1; | ||||||
|  |     decoder->decode = ImagingJpeg2KDecode; | ||||||
|  |     decoder->cleanup = ImagingJpeg2KDecodeCleanup; | ||||||
|  | 
 | ||||||
|  |     context = (JPEG2KDECODESTATE *)decoder->state.context; | ||||||
|  | 
 | ||||||
|  |     context->fd = fd; | ||||||
|  |     context->format = codec_format; | ||||||
|  |     context->reduce = reduce; | ||||||
|  |     context->layers = layers; | ||||||
|  | 
 | ||||||
|  |     return (PyObject*) decoder; | ||||||
|  | } | ||||||
|  | #endif /* HAVE_OPENJPEG */ | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								depends/install_openjpeg.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								depends/install_openjpeg.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | # install openjpeg | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if [ ! -f openjpeg-2.0.0.tar.gz ]; then | ||||||
|  |     wget 'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz'  | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | rm -r openjpeg-2.0.0 | ||||||
|  | tar -xvzf openjpeg-2.0.0.tar.gz | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | pushd openjpeg-2.0.0  | ||||||
|  | 
 | ||||||
|  | cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install | ||||||
|  | 
 | ||||||
|  | popd | ||||||
|  | 
 | ||||||
							
								
								
									
										18
									
								
								depends/install_webp.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								depends/install_webp.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | # install webp | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if [ ! -f libwebp-0.4.0.tar.gz ]; then | ||||||
|  |     wget 'https://webp.googlecode.com/files/libwebp-0.4.0.tar.gz' | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | rm -r libwebp-0.4.0 | ||||||
|  | tar -xvzf libwebp-0.4.0.tar.gz | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | pushd libwebp-0.4.0  | ||||||
|  | 
 | ||||||
|  | ./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make && sudo make install | ||||||
|  | 
 | ||||||
|  | popd | ||||||
|  | 
 | ||||||
|  | @ -153,6 +153,92 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: | ||||||
|     before building the Python Imaging Library. See the distribution README for |     before building the Python Imaging Library. See the distribution README for | ||||||
|     details. |     details. | ||||||
| 
 | 
 | ||||||
|  | JPEG 2000 | ||||||
|  | ^^^^^^^^^ | ||||||
|  | 
 | ||||||
|  | PIL reads and writes JPEG 2000 files containing ``L``, ``LA``, ``RGB`` or | ||||||
|  | ``RGBA`` data.  It can also read files containing ``YCbCr`` data, which it | ||||||
|  | converts on read into ``RGB`` or ``RGBA`` depending on whether or not there is | ||||||
|  | an alpha channel.  PIL supports JPEG 2000 raw codestreams (``.j2k`` files), as | ||||||
|  | well as boxed JPEG 2000 files (``.j2p`` or ``.jpx`` files).  PIL does *not* | ||||||
|  | support files whose components have different sampling frequencies. | ||||||
|  | 
 | ||||||
|  | When loading, if you set the ``mode`` on the image prior to the | ||||||
|  | :py:meth:`~PIL.Image.Image.load` method being invoked, you can ask PIL to | ||||||
|  | convert the image to either ``RGB`` or ``RGBA`` rather than choosing for | ||||||
|  | itself.  It is also possible to set ``reduce`` to the number of resolutions to | ||||||
|  | discard (each one reduces the size of the resulting image by a factor of 2), | ||||||
|  | and ``layers`` to specify the number of quality layers to load. | ||||||
|  | 
 | ||||||
|  | The :py:meth:`~PIL.Image.Image.save` method supports the following options: | ||||||
|  | 
 | ||||||
|  | **offset** | ||||||
|  |     The image offset, as a tuple of integers, e.g. (16, 16) | ||||||
|  | 
 | ||||||
|  | **tile_offset** | ||||||
|  |     The tile offset, again as a 2-tuple of integers. | ||||||
|  | 
 | ||||||
|  | **tile_size** | ||||||
|  |     The tile size as a 2-tuple.  If not specified, or if set to None, the | ||||||
|  |     image will be saved without tiling. | ||||||
|  | 
 | ||||||
|  | **quality_mode** | ||||||
|  |     Either `"rates"` or `"dB"` depending on the units you want to use to | ||||||
|  |     specify image quality. | ||||||
|  | 
 | ||||||
|  | **quality_layers** | ||||||
|  |     A sequence of numbers, each of which represents either an approximate size | ||||||
|  |     reduction (if quality mode is `"rates"`) or a signal to noise ratio value | ||||||
|  |     in decibels.  If not specified, defaults to a single layer of full quality. | ||||||
|  | 
 | ||||||
|  | **num_resolutions** | ||||||
|  |     The number of different image resolutions to be stored (which corresponds | ||||||
|  |     to the number of Discrete Wavelet Transform decompositions plus one). | ||||||
|  | 
 | ||||||
|  | **codeblock_size** | ||||||
|  |     The code-block size as a 2-tuple.  Minimum size is 4 x 4, maximum is 1024 x | ||||||
|  |     1024, with the additional restriction that no code-block may have more | ||||||
|  |     than 4096 coefficients (i.e. the product of the two numbers must be no | ||||||
|  |     greater than 4096). | ||||||
|  | 
 | ||||||
|  | **precinct_size** | ||||||
|  |     The precinct size as a 2-tuple.  Must be a power of two along both axes, | ||||||
|  |     and must be greater than the code-block size. | ||||||
|  | 
 | ||||||
|  | **irreversible** | ||||||
|  |     If ``True``, use the lossy Irreversible Color Transformation | ||||||
|  |     followed by DWT 9-7.  Defaults to ``False``, which means to use the | ||||||
|  |     Reversible Color Transformation with DWT 5-3. | ||||||
|  | 
 | ||||||
|  | **progression** | ||||||
|  |     Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``, | ||||||
|  |     ``"RPCL"``, ``"PCRL"``, ``"CPRL"``.  The letters stand for Component, | ||||||
|  |     Position, Resolution and Layer respectively and control the order of | ||||||
|  |     encoding, the idea being that e.g. an image encoded using LRCP mode can | ||||||
|  |     have its quality layers decoded as they arrive at the decoder, while one | ||||||
|  |     encoded using RLCP mode will have increasing resolutions decoded as they | ||||||
|  |     arrive, and so on. | ||||||
|  | 
 | ||||||
|  | **cinema_mode** | ||||||
|  |     Set the encoder to produce output compliant with the digital cinema | ||||||
|  |     specifications.  The options here are ``"no"`` (the default), | ||||||
|  |     ``"cinema2k-24"`` for 24fps 2K, ``"cinema2k-48"`` for 48fps 2K, and | ||||||
|  |     ``"cinema4k-24"`` for 24fps 4K.  Note that for compliant 2K files, | ||||||
|  |     *at least one* of your image dimensions must match 2048 x 1080, while | ||||||
|  |     for compliant 4K files, *at least one* of the dimensions must match | ||||||
|  |     4096 x 2160. | ||||||
|  | 
 | ||||||
|  | .. note:: | ||||||
|  | 
 | ||||||
|  |    To enable JPEG 2000 support, you need to build and install the OpenJPEG | ||||||
|  |    library, version 2.0.0 or higher, before building the Python Imaging | ||||||
|  |    Library. | ||||||
|  | 
 | ||||||
|  |    Windows users can install the OpenJPEG binaries available on the | ||||||
|  |    OpenJPEG website, but must add them to their PATH in order to use PIL (if | ||||||
|  |    you fail to do this, you will get errors about not being able to load the | ||||||
|  |    ``_imaging`` DLL). | ||||||
|  | 
 | ||||||
| MSP | MSP | ||||||
| ^^^ | ^^^ | ||||||
| 
 | 
 | ||||||
|  | @ -437,6 +523,25 @@ ICO | ||||||
| 
 | 
 | ||||||
| ICO is used to store icons on Windows. The largest available icon is read. | ICO is used to store icons on Windows. The largest available icon is read. | ||||||
| 
 | 
 | ||||||
|  | ICNS | ||||||
|  | ^^^^ | ||||||
|  | 
 | ||||||
|  | PIL reads Mac OS X ``.icns`` files.  By default, the largest available icon is | ||||||
|  | read, though you can override this by setting the :py:attr:`~PIL.Image.Image.size` | ||||||
|  | property before calling :py:meth:`~PIL.Image.Image.load`.  The | ||||||
|  | :py:meth:`~PIL.Image.Image.open` method sets the following | ||||||
|  | :py:attr:`~PIL.Image.Image.info` property: | ||||||
|  | 
 | ||||||
|  | **sizes** | ||||||
|  |     A list of supported sizes found in this icon file; these are a | ||||||
|  |     3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina | ||||||
|  |     icon and 1 for a standard icon.  You *are* permitted to use this 3-tuple | ||||||
|  |     format for the :py:attr:`~PIL.Image.Image.size` property if you set it | ||||||
|  |     before calling :py:meth:`~PIL.Image.Image.load`; after loading, the size | ||||||
|  |     will be reset to a 2-tuple containing pixel dimensions (so, e.g. if you | ||||||
|  |     ask for ``(512, 512, 2)``, the final value of | ||||||
|  |     :py:attr:`~PIL.Image.Image.size` will be ``(1024, 1024)``). | ||||||
|  | 
 | ||||||
| IMT | IMT | ||||||
| ^^^ | ^^^ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| Pillow: a modern fork of PIL | Pillow  | ||||||
| ============================ | ====== | ||||||
| 
 | 
 | ||||||
| Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the | Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the | ||||||
| Python Imaging Library by Fredrik Lundh and Contributors. | Python Imaging Library by Fredrik Lundh and Contributors. | ||||||
| 
 | 
 | ||||||
| .. image:: https://travis-ci.org/python-imaging/Pillow.png | .. image:: https://travis-ci.org/python-imaging/Pillow.png | ||||||
|  | @ -15,7 +15,7 @@ Python Imaging Library by Fredrik Lundh and Contributors. | ||||||
|     :target: https://pypi.python.org/pypi/Pillow/ |     :target: https://pypi.python.org/pypi/Pillow/ | ||||||
|     :alt: Number of PyPI downloads |     :alt: Number of PyPI downloads | ||||||
| 
 | 
 | ||||||
| To start using Pillow, read the :doc:`installation | To start using Pillow, please read the :doc:`installation | ||||||
| instructions <installation>`. | instructions <installation>`. | ||||||
| 
 | 
 | ||||||
| If you can't find the information you need, try the old `PIL Handbook`_, but be | If you can't find the information you need, try the old `PIL Handbook`_, but be | ||||||
|  |  | ||||||
|  | @ -172,6 +172,15 @@ Python Wheels | ||||||
| 
 | 
 | ||||||
|     $ pip install --use-wheel Pillow |     $ pip install --use-wheel Pillow | ||||||
| 
 | 
 | ||||||
|  | If the above does not work, it's likely because we haven't uploaded a | ||||||
|  | wheel for the latest version of Pillow. In that case, try pinning it | ||||||
|  | to a specific version: | ||||||
|  | 
 | ||||||
|  | :: | ||||||
|  | 
 | ||||||
|  |     $ pip install --use-wheel Pillow==2.3.0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| Platform support | Platform support | ||||||
| ---------------- | ---------------- | ||||||
| 
 | 
 | ||||||
|  | @ -216,4 +225,6 @@ current versions of Linux, OS X, and Windows. | ||||||
| +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | ||||||
| | Windows 8 Pro                    |Yes          | 2.6,2.7,3.2,3.3,3.4a3        | 2.2.0                        |x86,x86-64             | | | Windows 8 Pro                    |Yes          | 2.6,2.7,3.2,3.3,3.4a3        | 2.2.0                        |x86,x86-64             | | ||||||
| +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | ||||||
|  | | Windows 8.1 Pro                  |Yes          | 2.6,2.7,3.2,3.3,3.4          | 2.3.0                        |x86,x86-64             | | ||||||
|  | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -153,6 +153,14 @@ Plugin reference | ||||||
|     :undoc-members: |     :undoc-members: | ||||||
|     :show-inheritance: |     :show-inheritance: | ||||||
| 
 | 
 | ||||||
|  | :mod:`Jpeg2KImagePlugin` Module | ||||||
|  | ----------------------------- | ||||||
|  | 
 | ||||||
|  | .. automodule:: PIL.Jpeg2KImagePlugin | ||||||
|  |     :members: | ||||||
|  |     :undoc-members: | ||||||
|  |     :show-inheritance: | ||||||
|  | 
 | ||||||
| :mod:`McIdasImagePlugin` Module | :mod:`McIdasImagePlugin` Module | ||||||
| ------------------------------- | ------------------------------- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										160
									
								
								encode.c
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								encode.c
									
									
									
									
									
								
							|  | @ -40,6 +40,7 @@ typedef struct { | ||||||
|     PyObject_HEAD |     PyObject_HEAD | ||||||
|     int (*encode)(Imaging im, ImagingCodecState state, |     int (*encode)(Imaging im, ImagingCodecState state, | ||||||
|                   UINT8* buffer, int bytes); |                   UINT8* buffer, int bytes); | ||||||
|  |     int (*cleanup)(ImagingCodecState state); | ||||||
|     struct ImagingCodecStateInstance state; |     struct ImagingCodecStateInstance state; | ||||||
|     Imaging im; |     Imaging im; | ||||||
|     PyObject* lock; |     PyObject* lock; | ||||||
|  | @ -77,6 +78,9 @@ PyImaging_EncoderNew(int contextsize) | ||||||
|     /* Initialize encoder context */ |     /* Initialize encoder context */ | ||||||
|     encoder->state.context = context; |     encoder->state.context = context; | ||||||
| 
 | 
 | ||||||
|  |     /* Most encoders don't need this */ | ||||||
|  |     encoder->cleanup = NULL; | ||||||
|  | 
 | ||||||
|     /* Target image */ |     /* Target image */ | ||||||
|     encoder->lock = NULL; |     encoder->lock = NULL; | ||||||
|     encoder->im = NULL; |     encoder->im = NULL; | ||||||
|  | @ -87,6 +91,8 @@ PyImaging_EncoderNew(int contextsize) | ||||||
| static void | static void | ||||||
| _dealloc(ImagingEncoderObject* encoder) | _dealloc(ImagingEncoderObject* encoder) | ||||||
| { | { | ||||||
|  |     if (encoder->cleanup) | ||||||
|  |         encoder->cleanup(&encoder->state); | ||||||
|     free(encoder->state.buffer); |     free(encoder->state.buffer); | ||||||
|     free(encoder->state.context); |     free(encoder->state.context); | ||||||
|     Py_XDECREF(encoder->lock); |     Py_XDECREF(encoder->lock); | ||||||
|  | @ -797,4 +803,158 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* JPEG	2000								*/ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
| 
 | 
 | ||||||
|  | #ifdef HAVE_OPENJPEG | ||||||
|  | 
 | ||||||
|  | #include "Jpeg2K.h" | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_decode_coord_tuple(PyObject *tuple, int *x, int *y) | ||||||
|  | { | ||||||
|  |     *x = *y = 0; | ||||||
|  | 
 | ||||||
|  |     if (tuple && PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2) { | ||||||
|  |         *x = (int)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 0)); | ||||||
|  |         *y = (int)PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1)); | ||||||
|  | 
 | ||||||
|  |         if (*x < 0) | ||||||
|  |             *x = 0; | ||||||
|  |         if (*y < 0) | ||||||
|  |             *y = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PyObject* | ||||||
|  | PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) | ||||||
|  | { | ||||||
|  |     ImagingEncoderObject *encoder; | ||||||
|  |     JPEG2KENCODESTATE *context; | ||||||
|  | 
 | ||||||
|  |     char *mode; | ||||||
|  |     char *format; | ||||||
|  |     OPJ_CODEC_FORMAT codec_format; | ||||||
|  |     PyObject *offset = NULL, *tile_offset = NULL, *tile_size = NULL; | ||||||
|  |     char *quality_mode = "rates"; | ||||||
|  |     PyObject *quality_layers = NULL; | ||||||
|  |     int num_resolutions = 0; | ||||||
|  |     PyObject *cblk_size = NULL, *precinct_size = NULL; | ||||||
|  |     PyObject *irreversible = NULL; | ||||||
|  |     char *progression = "LRCP"; | ||||||
|  |     OPJ_PROG_ORDER prog_order; | ||||||
|  |     char *cinema_mode = "no"; | ||||||
|  |     OPJ_CINEMA_MODE cine_mode; | ||||||
|  |     int fd = -1; | ||||||
|  | 
 | ||||||
|  |     if (!PyArg_ParseTuple(args, "ss|OOOsOIOOOssi", &mode, &format, | ||||||
|  |                           &offset, &tile_offset, &tile_size, | ||||||
|  |                           &quality_mode, &quality_layers, &num_resolutions, | ||||||
|  |                           &cblk_size, &precinct_size, | ||||||
|  |                           &irreversible, &progression, &cinema_mode, | ||||||
|  |                           &fd)) | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     if (strcmp (format, "j2k") == 0) | ||||||
|  |         codec_format = OPJ_CODEC_J2K; | ||||||
|  |     else if (strcmp (format, "jpt") == 0) | ||||||
|  |         codec_format = OPJ_CODEC_JPT; | ||||||
|  |     else if (strcmp (format, "jp2") == 0) | ||||||
|  |         codec_format = OPJ_CODEC_JP2; | ||||||
|  |     else | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     if (strcmp(progression, "LRCP") == 0) | ||||||
|  |         prog_order = OPJ_LRCP; | ||||||
|  |     else if (strcmp(progression, "RLCP") == 0) | ||||||
|  |         prog_order = OPJ_RLCP; | ||||||
|  |     else if (strcmp(progression, "RPCL") == 0) | ||||||
|  |         prog_order = OPJ_RPCL; | ||||||
|  |     else if (strcmp(progression, "PCRL") == 0) | ||||||
|  |         prog_order = OPJ_PCRL; | ||||||
|  |     else if (strcmp(progression, "CPRL") == 0) | ||||||
|  |         prog_order = OPJ_CPRL; | ||||||
|  |     else | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     if (strcmp(cinema_mode, "no") == 0) | ||||||
|  |         cine_mode = OPJ_OFF; | ||||||
|  |     else if (strcmp(cinema_mode, "cinema2k-24") == 0) | ||||||
|  |         cine_mode = OPJ_CINEMA2K_24; | ||||||
|  |     else if (strcmp(cinema_mode, "cinema2k-48") == 0) | ||||||
|  |         cine_mode = OPJ_CINEMA2K_48; | ||||||
|  |     else if (strcmp(cinema_mode, "cinema4k-24") == 0) | ||||||
|  |         cine_mode = OPJ_CINEMA4K_24; | ||||||
|  |     else | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     encoder = PyImaging_EncoderNew(sizeof(JPEG2KENCODESTATE)); | ||||||
|  |     if (!encoder) | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     encoder->encode = ImagingJpeg2KEncode; | ||||||
|  |     encoder->cleanup = ImagingJpeg2KEncodeCleanup; | ||||||
|  | 
 | ||||||
|  |     context = (JPEG2KENCODESTATE *)encoder->state.context; | ||||||
|  | 
 | ||||||
|  |     context->fd = fd; | ||||||
|  |     context->format = codec_format; | ||||||
|  |     context->offset_x = context->offset_y = 0; | ||||||
|  | 
 | ||||||
|  |     j2k_decode_coord_tuple(offset, &context->offset_x, &context->offset_y); | ||||||
|  |     j2k_decode_coord_tuple(tile_offset, | ||||||
|  |                            &context->tile_offset_x, | ||||||
|  |                            &context->tile_offset_y); | ||||||
|  |     j2k_decode_coord_tuple(tile_size,  | ||||||
|  |                            &context->tile_size_x, | ||||||
|  |                            &context->tile_size_y); | ||||||
|  | 
 | ||||||
|  |     /* Error on illegal tile offsets */ | ||||||
|  |     if (context->tile_size_x && context->tile_size_y) { | ||||||
|  |         if (context->tile_offset_x <= context->offset_x - context->tile_size_x | ||||||
|  |             || context->tile_offset_y <= context->offset_y - context->tile_size_y) { | ||||||
|  |             PyErr_SetString(PyExc_ValueError, | ||||||
|  |                             "JPEG 2000 tile offset too small; top left tile must " | ||||||
|  |                             "intersect image area"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (context->tile_offset_x > context->offset_x | ||||||
|  |             || context->tile_offset_y > context->offset_y) { | ||||||
|  |             PyErr_SetString(PyExc_ValueError,  | ||||||
|  |                             "JPEG 2000 tile offset too large to cover image area"); | ||||||
|  |             Py_DECREF(encoder); | ||||||
|  |             return NULL; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (quality_layers && PySequence_Check(quality_layers)) { | ||||||
|  |         context->quality_is_in_db = strcmp (quality_mode, "dB") == 0; | ||||||
|  |         context->quality_layers = quality_layers; | ||||||
|  |         Py_INCREF(quality_layers); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     context->num_resolutions = num_resolutions; | ||||||
|  | 
 | ||||||
|  |     j2k_decode_coord_tuple(cblk_size, | ||||||
|  |                            &context->cblk_width, | ||||||
|  |                            &context->cblk_height); | ||||||
|  |     j2k_decode_coord_tuple(precinct_size, | ||||||
|  |                            &context->precinct_width, | ||||||
|  |                            &context->precinct_height); | ||||||
|  | 
 | ||||||
|  |     context->irreversible = PyObject_IsTrue(irreversible); | ||||||
|  |     context->progression = prog_order; | ||||||
|  |     context->cinema_mode = cine_mode; | ||||||
|  | 
 | ||||||
|  |     return (PyObject *)encoder; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Local Variables: | ||||||
|  |  * c-basic-offset: 4 | ||||||
|  |  * End: | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | @ -424,6 +424,14 @@ extern int ImagingJpegDecodeCleanup(ImagingCodecState state); | ||||||
| extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, | extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, | ||||||
| 			     UINT8* buffer, int bytes); | 			     UINT8* buffer, int bytes); | ||||||
| #endif | #endif | ||||||
|  | #ifdef HAVE_OPENJPEG | ||||||
|  | extern int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, | ||||||
|  |                                UINT8* buffer, int bytes); | ||||||
|  | extern int ImagingJpeg2KDecodeCleanup(ImagingCodecState state); | ||||||
|  | extern int ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, | ||||||
|  |                                UINT8* buffer, int bytes); | ||||||
|  | extern int ImagingJpeg2KEncodeCleanup(ImagingCodecState state); | ||||||
|  | #endif | ||||||
| extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, | extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, | ||||||
| 			    UINT8* buffer, int bytes); | 			    UINT8* buffer, int bytes); | ||||||
| #ifdef	HAVE_LIBTIFF | #ifdef	HAVE_LIBTIFF | ||||||
|  | @ -497,6 +505,32 @@ struct ImagingCodecStateInstance { | ||||||
|     void *context; |     void *context; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /* Incremental encoding/decoding support */ | ||||||
|  | typedef struct ImagingIncrementalCodecStruct *ImagingIncrementalCodec; | ||||||
|  | 
 | ||||||
|  | typedef int (*ImagingIncrementalCodecEntry)(Imaging im,  | ||||||
|  |                                             ImagingCodecState state, | ||||||
|  |                                             ImagingIncrementalCodec codec); | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |   INCREMENTAL_CODEC_READ = 1, | ||||||
|  |   INCREMENTAL_CODEC_WRITE = 2 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |   INCREMENTAL_CODEC_NOT_SEEKABLE = 0, | ||||||
|  |   INCREMENTAL_CODEC_SEEKABLE = 1 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern ImagingIncrementalCodec ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, Imaging im, ImagingCodecState state, int read_or_write, int seekable, int fd); | ||||||
|  | extern void ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec); | ||||||
|  | extern int ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, UINT8 *buf, int bytes); | ||||||
|  | extern ssize_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes); | ||||||
|  | extern off_t ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, off_t bytes); | ||||||
|  | extern ssize_t ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, const void *buffer, size_t bytes); | ||||||
|  | extern off_t ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec, off_t bytes); | ||||||
|  | extern size_t ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec); | ||||||
|  | 
 | ||||||
| /* Errcodes */ | /* Errcodes */ | ||||||
| #define	IMAGING_CODEC_END	 1 | #define	IMAGING_CODEC_END	 1 | ||||||
| #define	IMAGING_CODEC_OVERRUN	-1 | #define	IMAGING_CODEC_OVERRUN	-1 | ||||||
|  |  | ||||||
							
								
								
									
										677
									
								
								libImaging/Incremental.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										677
									
								
								libImaging/Incremental.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,677 @@ | ||||||
|  | /*
 | ||||||
|  |  * The Python Imaging Library | ||||||
|  |  * $Id$ | ||||||
|  |  * | ||||||
|  |  * incremental decoding adaptor. | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2014 Coriolis Systems Limited | ||||||
|  |  * Copyright (c) 2014 Alastair Houghton | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "Imaging.h" | ||||||
|  | 
 | ||||||
|  | /* The idea behind this interface is simple: the actual decoding proceeds in
 | ||||||
|  |    a thread, which is run in lock step with the main thread.  Whenever the | ||||||
|  |    ImagingIncrementalCodecRead() call runs short on data, it suspends the | ||||||
|  |    decoding thread and wakes the main thread.  Conversely, the | ||||||
|  |    ImagingIncrementalCodecPushBuffer() call suspends the main thread and wakes | ||||||
|  |    the decoding thread, providing a buffer of data. | ||||||
|  | 
 | ||||||
|  |    The two threads are never running simultaneously, so there is no need for | ||||||
|  |    any addition synchronisation measures outside of this file. | ||||||
|  | 
 | ||||||
|  |    Note also that we start the thread suspended (on Windows), or make it | ||||||
|  |    immediately wait (other platforms), so that it's possible to initialise | ||||||
|  |    things before the thread starts running. | ||||||
|  | 
 | ||||||
|  |    This interface is useful to allow PIL to interact efficiently with any | ||||||
|  |    third-party imaging library that does not support suspendable reads; | ||||||
|  |    one example is OpenJPEG (which is used for J2K support).  The TIFF library | ||||||
|  |    might also benefit from using this code. | ||||||
|  | 
 | ||||||
|  |    Note that if using this module, you want to set handles_eof on your | ||||||
|  |    decoder to true.  Why?  Because otherwise ImageFile.load() will abort, | ||||||
|  |    thinking that the image is truncated, whereas generally you want it to | ||||||
|  |    pass the EOF condition (0 bytes to read) through to your code. */ | ||||||
|  | 
 | ||||||
|  | /* Additional complication: *Some* codecs need to seek; this is fine if
 | ||||||
|  |    there is a file descriptor, but if we're buffering data it becomes | ||||||
|  |    awkward.  The incremental adaptor now contains code to handle these | ||||||
|  |    two cases. */ | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include <windows.h> | ||||||
|  | #include <process.h> | ||||||
|  | #else | ||||||
|  | #include <pthread.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define DEBUG_INCREMENTAL 0 | ||||||
|  | 
 | ||||||
|  | #if DEBUG_INCREMENTAL | ||||||
|  | #define DEBUG(...) printf(__VA_ARGS__) | ||||||
|  | #else | ||||||
|  | #define DEBUG(...) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | struct ImagingIncrementalCodecStruct { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |   HANDLE            hCodecEvent; | ||||||
|  |   HANDLE            hDataEvent; | ||||||
|  |   HANDLE            hThread; | ||||||
|  | #else | ||||||
|  |   pthread_mutex_t   start_mutex; | ||||||
|  |   pthread_cond_t    start_cond; | ||||||
|  |   pthread_mutex_t   codec_mutex; | ||||||
|  |   pthread_cond_t    codec_cond; | ||||||
|  |   pthread_mutex_t   data_mutex; | ||||||
|  |   pthread_cond_t    data_cond; | ||||||
|  |   pthread_t         thread; | ||||||
|  | #endif | ||||||
|  |   ImagingIncrementalCodecEntry  entry; | ||||||
|  |   Imaging                       im; | ||||||
|  |   ImagingCodecState             state; | ||||||
|  |   struct { | ||||||
|  |     int    fd; | ||||||
|  |     UINT8 *buffer;      /* Base of buffer */ | ||||||
|  |     UINT8 *ptr;         /* Current pointer in buffer */ | ||||||
|  |     UINT8 *top;         /* Highest point in buffer we've used */ | ||||||
|  |     UINT8 *end;         /* End of buffer */ | ||||||
|  |   } stream; | ||||||
|  |   int                           read_or_write; | ||||||
|  |   int                           seekable; | ||||||
|  |   int                           started; | ||||||
|  |   int                           result; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void flush_stream(ImagingIncrementalCodec codec); | ||||||
|  | 
 | ||||||
|  | #if _WIN32 | ||||||
|  | static unsigned int __stdcall | ||||||
|  | codec_thread(void *ptr) | ||||||
|  | { | ||||||
|  |   ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr; | ||||||
|  | 
 | ||||||
|  |   DEBUG("Entering thread\n"); | ||||||
|  | 
 | ||||||
|  |   codec->result = codec->entry(codec->im, codec->state, codec); | ||||||
|  | 
 | ||||||
|  |   DEBUG("Leaving thread (%d)\n", codec->result); | ||||||
|  | 
 | ||||||
|  |   flush_stream(codec); | ||||||
|  | 
 | ||||||
|  |   SetEvent(codec->hCodecEvent); | ||||||
|  | 
 | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | static void * | ||||||
|  | codec_thread(void *ptr) | ||||||
|  | { | ||||||
|  |   ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr; | ||||||
|  | 
 | ||||||
|  |   DEBUG("Entering thread\n"); | ||||||
|  | 
 | ||||||
|  |   codec->result = codec->entry(codec->im, codec->state, codec); | ||||||
|  | 
 | ||||||
|  |   DEBUG("Leaving thread (%d)\n", codec->result); | ||||||
|  | 
 | ||||||
|  |   flush_stream(codec); | ||||||
|  | 
 | ||||||
|  |   pthread_mutex_lock(&codec->codec_mutex); | ||||||
|  |   pthread_cond_signal(&codec->codec_cond); | ||||||
|  |   pthread_mutex_unlock(&codec->codec_mutex); | ||||||
|  | 
 | ||||||
|  |   return NULL; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | flush_stream(ImagingIncrementalCodec codec) | ||||||
|  | { | ||||||
|  |   UINT8 *buffer; | ||||||
|  |   size_t bytes; | ||||||
|  | 
 | ||||||
|  |   /* This is to flush data from the write buffer for a seekable write
 | ||||||
|  |      codec. */ | ||||||
|  |   if (codec->read_or_write != INCREMENTAL_CODEC_WRITE | ||||||
|  |       || codec->state->errcode != IMAGING_CODEC_END | ||||||
|  |       || !codec->seekable | ||||||
|  |       || codec->stream.fd >= 0) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   DEBUG("flushing data\n"); | ||||||
|  | 
 | ||||||
|  |   buffer = codec->stream.buffer; | ||||||
|  |   bytes = codec->stream.ptr - codec->stream.buffer; | ||||||
|  | 
 | ||||||
|  |   codec->state->errcode = 0; | ||||||
|  |   codec->seekable = INCREMENTAL_CODEC_NOT_SEEKABLE; | ||||||
|  |   codec->stream.buffer = codec->stream.ptr = codec->stream.end | ||||||
|  |     = codec->stream.top = NULL; | ||||||
|  | 
 | ||||||
|  |   ImagingIncrementalCodecWrite(codec, buffer, bytes); | ||||||
|  | 
 | ||||||
|  |   codec->state->errcode = IMAGING_CODEC_END; | ||||||
|  |   codec->result = (int)ImagingIncrementalCodecBytesInBuffer(codec); | ||||||
|  | 
 | ||||||
|  |   free(buffer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Create a new incremental codec */ | ||||||
|  | ImagingIncrementalCodec | ||||||
|  | ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry, | ||||||
|  |                               Imaging im, | ||||||
|  |                               ImagingCodecState state, | ||||||
|  |                               int read_or_write, | ||||||
|  |                               int seekable, | ||||||
|  |                               int fd) | ||||||
|  | { | ||||||
|  |   ImagingIncrementalCodec codec = (ImagingIncrementalCodec)malloc(sizeof(struct ImagingIncrementalCodecStruct)); | ||||||
|  | 
 | ||||||
|  |   codec->entry = codec_entry; | ||||||
|  |   codec->im = im; | ||||||
|  |   codec->state = state; | ||||||
|  |   codec->result = 0; | ||||||
|  |   codec->stream.fd = fd; | ||||||
|  |   codec->stream.buffer = codec->stream.ptr = codec->stream.end  | ||||||
|  |     = codec->stream.top = NULL; | ||||||
|  |   codec->started = 0; | ||||||
|  |   codec->seekable = seekable; | ||||||
|  |   codec->read_or_write = read_or_write; | ||||||
|  | 
 | ||||||
|  |   if (fd >= 0) | ||||||
|  |     lseek(fd, 0, SEEK_SET); | ||||||
|  | 
 | ||||||
|  |   /* System specific set-up */ | ||||||
|  | #if _WIN32 | ||||||
|  |   codec->hCodecEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | ||||||
|  | 
 | ||||||
|  |   if (!codec->hCodecEvent) { | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   codec->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | ||||||
|  | 
 | ||||||
|  |   if (!codec->hDataEvent) { | ||||||
|  |     CloseHandle(codec->hCodecEvent); | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   codec->hThread = _beginthreadex(NULL, 0, codec_thread, codec, | ||||||
|  |                                     CREATE_SUSPENDED, NULL); | ||||||
|  | 
 | ||||||
|  |   if (!codec->hThread) { | ||||||
|  |     CloseHandle(codec->hCodecEvent); | ||||||
|  |     CloseHandle(codec->hDataEvent); | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | #else | ||||||
|  |   if (pthread_mutex_init(&codec->start_mutex, NULL)) { | ||||||
|  |     free (codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_mutex_init(&codec->codec_mutex, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&codec->start_mutex); | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_mutex_init(&codec->data_mutex, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&codec->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->codec_mutex); | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_cond_init(&codec->start_cond, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&codec->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->codec_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->data_mutex); | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_cond_init(&codec->codec_cond, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&codec->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->codec_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->data_mutex); | ||||||
|  |     pthread_cond_destroy(&codec->start_cond); | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_cond_init(&codec->data_cond, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&codec->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->codec_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->data_mutex); | ||||||
|  |     pthread_cond_destroy(&codec->start_cond); | ||||||
|  |     pthread_cond_destroy(&codec->codec_cond); | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_create(&codec->thread, NULL, codec_thread, codec)) { | ||||||
|  |     pthread_mutex_destroy(&codec->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->codec_mutex); | ||||||
|  |     pthread_mutex_destroy(&codec->data_mutex); | ||||||
|  |     pthread_cond_destroy(&codec->start_cond); | ||||||
|  |     pthread_cond_destroy(&codec->codec_cond); | ||||||
|  |     pthread_cond_destroy(&codec->data_cond); | ||||||
|  |     free(codec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   return codec; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Destroy an incremental codec */ | ||||||
|  | void | ||||||
|  | ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec) | ||||||
|  | { | ||||||
|  |   DEBUG("destroying\n"); | ||||||
|  | 
 | ||||||
|  |   if (!codec->started) { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     ResumeThread(codec->hThread); | ||||||
|  | #else | ||||||
|  |     pthread_cond_signal(&codec->start_cond); | ||||||
|  | #endif | ||||||
|  |     codec->started = 1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_lock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   if (codec->seekable && codec->stream.fd < 0) | ||||||
|  |     free (codec->stream.buffer); | ||||||
|  | 
 | ||||||
|  |   codec->stream.buffer = codec->stream.ptr = codec->stream.end  | ||||||
|  |     = codec->stream.top = NULL; | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  |   SetEvent(codec->hDataEvent); | ||||||
|  | 
 | ||||||
|  |   WaitForSingleObject(codec->hThread, INFINITE); | ||||||
|  | 
 | ||||||
|  |   CloseHandle(codec->hThread); | ||||||
|  |   CloseHandle(codec->hCodecEvent); | ||||||
|  |   CloseHandle(codec->hDataEvent); | ||||||
|  | #else | ||||||
|  |   pthread_cond_signal(&codec->data_cond); | ||||||
|  |   pthread_mutex_unlock(&codec->data_mutex); | ||||||
|  | 
 | ||||||
|  |   pthread_join(codec->thread, NULL); | ||||||
|  | 
 | ||||||
|  |   pthread_mutex_destroy(&codec->start_mutex); | ||||||
|  |   pthread_mutex_destroy(&codec->codec_mutex); | ||||||
|  |   pthread_mutex_destroy(&codec->data_mutex); | ||||||
|  |   pthread_cond_destroy(&codec->start_cond); | ||||||
|  |   pthread_cond_destroy(&codec->codec_cond); | ||||||
|  |   pthread_cond_destroy(&codec->data_cond); | ||||||
|  | #endif | ||||||
|  |   free (codec); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Push a data buffer for an incremental codec */ | ||||||
|  | int | ||||||
|  | ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec, | ||||||
|  |                                   UINT8 *buf, int bytes) | ||||||
|  | { | ||||||
|  |   if (!codec->started) { | ||||||
|  |     DEBUG("starting\n"); | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     ResumeThread(codec->hThread); | ||||||
|  | #else | ||||||
|  |     pthread_cond_signal(&codec->start_cond); | ||||||
|  | #endif | ||||||
|  |     codec->started = 1; | ||||||
|  | 
 | ||||||
|  |     /* Wait for the thread to ask for data */ | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     WaitForSingleObject(codec->hCodecEvent, INFINITE); | ||||||
|  | #else | ||||||
|  |     pthread_mutex_lock(&codec->codec_mutex); | ||||||
|  |     pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex); | ||||||
|  |     pthread_mutex_unlock(&codec->codec_mutex); | ||||||
|  | #endif | ||||||
|  |     if (codec->result < 0) { | ||||||
|  |       DEBUG("got result %d\n", codec->result); | ||||||
|  | 
 | ||||||
|  |       return codec->result; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /* Codecs using an fd don't need data, so when we get here, we're done */ | ||||||
|  |   if (codec->stream.fd >= 0) { | ||||||
|  |     DEBUG("got result %d\n", codec->result); | ||||||
|  | 
 | ||||||
|  |     return codec->result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   DEBUG("providing %p, %d\n", buf, bytes); | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_lock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   if (codec->read_or_write == INCREMENTAL_CODEC_READ | ||||||
|  |       && codec->seekable && codec->stream.fd < 0) { | ||||||
|  |     /* In this specific case, we append to a buffer we allocate ourselves */ | ||||||
|  |     size_t old_size = codec->stream.end - codec->stream.buffer; | ||||||
|  |     size_t new_size = codec->stream.end - codec->stream.buffer + bytes; | ||||||
|  |     UINT8 *new = (UINT8 *)realloc (codec->stream.buffer, new_size); | ||||||
|  | 
 | ||||||
|  |     if (!new) { | ||||||
|  |       codec->state->errcode = IMAGING_CODEC_MEMORY; | ||||||
|  | #ifndef _WIN32 | ||||||
|  |       pthread_mutex_unlock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  |       return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new; | ||||||
|  |     codec->stream.end = new + new_size; | ||||||
|  |     codec->stream.buffer = new; | ||||||
|  | 
 | ||||||
|  |     memcpy(new + old_size, buf, bytes); | ||||||
|  |   } else { | ||||||
|  |     codec->stream.buffer = codec->stream.ptr = buf; | ||||||
|  |     codec->stream.end = buf + bytes; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  |   SetEvent(codec->hDataEvent); | ||||||
|  |   WaitForSingleObject(codec->hCodecEvent, INFINITE); | ||||||
|  | #else | ||||||
|  |   pthread_cond_signal(&codec->data_cond); | ||||||
|  |   pthread_mutex_unlock(&codec->data_mutex); | ||||||
|  | 
 | ||||||
|  |   pthread_mutex_lock(&codec->codec_mutex); | ||||||
|  |   pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex); | ||||||
|  |   pthread_mutex_unlock(&codec->codec_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   DEBUG("got result %d\n", codec->result); | ||||||
|  | 
 | ||||||
|  |   return codec->result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t | ||||||
|  | ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec) | ||||||
|  | { | ||||||
|  |   return codec->stream.ptr - codec->stream.buffer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ssize_t | ||||||
|  | ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, | ||||||
|  |                              void *buffer, size_t bytes) | ||||||
|  | { | ||||||
|  |   UINT8 *ptr = (UINT8 *)buffer; | ||||||
|  |   size_t done = 0; | ||||||
|  | 
 | ||||||
|  |   if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) { | ||||||
|  |     DEBUG("attempt to read from write codec\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes); | ||||||
|  | 
 | ||||||
|  |   if (codec->stream.fd >= 0) { | ||||||
|  |     ssize_t ret = read(codec->stream.fd, buffer, bytes); | ||||||
|  |     DEBUG("read %lld bytes from fd\n", (long long)ret); | ||||||
|  |     return ret; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_lock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  |   while (bytes) { | ||||||
|  |     size_t todo = bytes; | ||||||
|  |     size_t remaining = codec->stream.end - codec->stream.ptr; | ||||||
|  | 
 | ||||||
|  |     if (!remaining) { | ||||||
|  |       DEBUG("waiting for data\n"); | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |       pthread_mutex_lock(&codec->codec_mutex); | ||||||
|  | #endif | ||||||
|  |       codec->result = (int)(codec->stream.ptr - codec->stream.buffer); | ||||||
|  | #if _WIN32 | ||||||
|  |       SetEvent(codec->hCodecEvent); | ||||||
|  |       WaitForSingleObject(codec->hDataEvent, INFINITE); | ||||||
|  | #else | ||||||
|  |       pthread_cond_signal(&codec->codec_cond); | ||||||
|  |       pthread_mutex_unlock(&codec->codec_mutex); | ||||||
|  |       pthread_cond_wait(&codec->data_cond, &codec->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |       remaining = codec->stream.end - codec->stream.ptr; | ||||||
|  |       codec->stream.top = codec->stream.end; | ||||||
|  | 
 | ||||||
|  |       DEBUG("got %llu bytes\n", (unsigned long long)remaining); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (todo > remaining) | ||||||
|  |       todo = remaining; | ||||||
|  | 
 | ||||||
|  |     if (!todo) | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     memcpy (ptr, codec->stream.ptr, todo); | ||||||
|  |     codec->stream.ptr += todo; | ||||||
|  |     bytes -= todo; | ||||||
|  |     done += todo; | ||||||
|  |     ptr += todo; | ||||||
|  |   } | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_unlock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   DEBUG("read total %llu bytes\n", (unsigned long long)done); | ||||||
|  | 
 | ||||||
|  |   return done; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | off_t | ||||||
|  | ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, | ||||||
|  |                             off_t bytes) | ||||||
|  | { | ||||||
|  |   off_t done = 0; | ||||||
|  | 
 | ||||||
|  |   DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes); | ||||||
|  | 
 | ||||||
|  |   /* In write mode, explicitly fill with zeroes */ | ||||||
|  |   if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) { | ||||||
|  |     static const UINT8 zeroes[256] = { 0 }; | ||||||
|  |     off_t done = 0; | ||||||
|  |     while (bytes) { | ||||||
|  |       size_t todo = (size_t)(bytes > 256 ? 256 : bytes); | ||||||
|  |       ssize_t written = ImagingIncrementalCodecWrite(codec, zeroes, todo); | ||||||
|  |       if (written <= 0) | ||||||
|  |         break; | ||||||
|  |       done += written; | ||||||
|  |       bytes -= written; | ||||||
|  |     } | ||||||
|  |     return done; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (codec->stream.fd >= 0) | ||||||
|  |     return lseek(codec->stream.fd, bytes, SEEK_CUR); | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_lock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  |   while (bytes) { | ||||||
|  |     off_t todo = bytes; | ||||||
|  |     off_t remaining = codec->stream.end - codec->stream.ptr; | ||||||
|  | 
 | ||||||
|  |     if (!remaining) { | ||||||
|  |       DEBUG("waiting for data\n"); | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |       pthread_mutex_lock(&codec->codec_mutex); | ||||||
|  | #endif | ||||||
|  |       codec->result = (int)(codec->stream.ptr - codec->stream.buffer); | ||||||
|  | #if _WIN32 | ||||||
|  |       SetEvent(codec->hCodecEvent); | ||||||
|  |       WaitForSingleObject(codec->hDataEvent, INFINITE); | ||||||
|  | #else | ||||||
|  |       pthread_cond_signal(&codec->codec_cond); | ||||||
|  |       pthread_mutex_unlock(&codec->codec_mutex); | ||||||
|  |       pthread_cond_wait(&codec->data_cond, &codec->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |       remaining = codec->stream.end - codec->stream.ptr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (todo > remaining) | ||||||
|  |       todo = remaining; | ||||||
|  | 
 | ||||||
|  |     if (!todo) | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     codec->stream.ptr += todo; | ||||||
|  |     bytes -= todo; | ||||||
|  |     done += todo; | ||||||
|  |   } | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_unlock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   DEBUG("skipped total %llu bytes\n", (unsigned long long)done); | ||||||
|  | 
 | ||||||
|  |   return done; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ssize_t | ||||||
|  | ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec, | ||||||
|  |                              const void *buffer, size_t bytes) | ||||||
|  | { | ||||||
|  |   const UINT8 *ptr = (const UINT8 *)buffer; | ||||||
|  |   size_t done = 0; | ||||||
|  | 
 | ||||||
|  |   if (codec->read_or_write == INCREMENTAL_CODEC_READ) { | ||||||
|  |     DEBUG("attempt to write from read codec\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   DEBUG("write (have %llu bytes)\n", (unsigned long long)bytes); | ||||||
|  | 
 | ||||||
|  |   if (codec->stream.fd >= 0) | ||||||
|  |     return write(codec->stream.fd, buffer, bytes); | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_lock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  |   while (bytes) { | ||||||
|  |     size_t todo = bytes; | ||||||
|  |     size_t remaining = codec->stream.end - codec->stream.ptr; | ||||||
|  | 
 | ||||||
|  |     if (!remaining) { | ||||||
|  |       if (codec->seekable && codec->stream.fd < 0) { | ||||||
|  |         /* In this case, we maintain the stream buffer ourselves */ | ||||||
|  |         size_t old_size = codec->stream.top - codec->stream.buffer; | ||||||
|  |         size_t new_size = (old_size + bytes + 65535) & ~65535; | ||||||
|  |         UINT8 *new = (UINT8 *)realloc(codec->stream.buffer, new_size); | ||||||
|  | 
 | ||||||
|  |         if (!new) { | ||||||
|  |           codec->state->errcode = IMAGING_CODEC_MEMORY; | ||||||
|  | #ifndef _WIN32 | ||||||
|  |           pthread_mutex_unlock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  |           return done == 0 ? -1 : done; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new; | ||||||
|  |         codec->stream.buffer = new; | ||||||
|  |         codec->stream.end = new + new_size; | ||||||
|  |         codec->stream.top = new + old_size; | ||||||
|  |       } else { | ||||||
|  |         DEBUG("waiting for space\n"); | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |         pthread_mutex_lock(&codec->codec_mutex); | ||||||
|  | #endif | ||||||
|  |         codec->result = (int)(codec->stream.ptr - codec->stream.buffer); | ||||||
|  | #if _WIN32 | ||||||
|  |         SetEvent(codec->hCodecEvent); | ||||||
|  |         WaitForSingleObject(codec->hDataEvent, INFINITE); | ||||||
|  | #else | ||||||
|  |         pthread_cond_signal(&codec->codec_cond); | ||||||
|  |         pthread_mutex_unlock(&codec->codec_mutex); | ||||||
|  |         pthread_cond_wait(&codec->data_cond, &codec->data_mutex); | ||||||
|  | #endif | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       remaining = codec->stream.end - codec->stream.ptr; | ||||||
|  | 
 | ||||||
|  |       DEBUG("got %llu bytes\n", (unsigned long long)remaining); | ||||||
|  |     } | ||||||
|  |     if (todo > remaining) | ||||||
|  |       todo = remaining; | ||||||
|  | 
 | ||||||
|  |     if (!todo) | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     memcpy (codec->stream.ptr, ptr, todo); | ||||||
|  |     codec->stream.ptr += todo; | ||||||
|  |     bytes -= todo; | ||||||
|  |     done += todo; | ||||||
|  |     ptr += todo; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (codec->stream.ptr > codec->stream.top) | ||||||
|  |     codec->stream.top = codec->stream.ptr; | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_unlock(&codec->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   DEBUG("wrote total %llu bytes\n", (unsigned long long)done); | ||||||
|  | 
 | ||||||
|  |   return done; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | off_t | ||||||
|  | ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec, | ||||||
|  |                             off_t bytes) | ||||||
|  | { | ||||||
|  |   off_t buffered; | ||||||
|  | 
 | ||||||
|  |   DEBUG("seeking (going to %llu bytes)\n", (unsigned long long)bytes); | ||||||
|  | 
 | ||||||
|  |   if (codec->stream.fd >= 0) | ||||||
|  |     return lseek(codec->stream.fd, bytes, SEEK_SET); | ||||||
|  | 
 | ||||||
|  |   if (!codec->seekable) { | ||||||
|  |     DEBUG("attempt to seek non-seekable stream\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (bytes < 0) { | ||||||
|  |     DEBUG("attempt to seek before stream start\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |      | ||||||
|  |   buffered = codec->stream.top - codec->stream.buffer; | ||||||
|  | 
 | ||||||
|  |   if (bytes <= buffered) { | ||||||
|  |     DEBUG("seek within buffer\n"); | ||||||
|  |     codec->stream.ptr = codec->stream.buffer + bytes; | ||||||
|  |     return bytes; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return buffered + ImagingIncrementalCodecSkip(codec, bytes - buffered); | ||||||
|  | } | ||||||
							
								
								
									
										91
									
								
								libImaging/Jpeg2K.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								libImaging/Jpeg2K.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | ||||||
|  | /*
 | ||||||
|  |  * The Python Imaging Library | ||||||
|  |  * $Id$ | ||||||
|  |  * | ||||||
|  |  * declarations for the OpenJPEG codec interface. | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2014 by Coriolis Systems Limited | ||||||
|  |  * Copyright (c) 2014 by Alastair Houghton | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <openjpeg-2.0/openjpeg.h> | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Decoder								*/ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     /* CONFIGURATION */ | ||||||
|  | 
 | ||||||
|  |     /* File descriptor, if available; otherwise, -1 */ | ||||||
|  |     int fd; | ||||||
|  | 
 | ||||||
|  |     /* Specify the desired format */ | ||||||
|  |     OPJ_CODEC_FORMAT format; | ||||||
|  | 
 | ||||||
|  |     /* Set to divide image resolution by 2**reduce. */ | ||||||
|  |     int            reduce; | ||||||
|  | 
 | ||||||
|  |     /* Set to limit the number of quality layers to decode (0 = all layers) */ | ||||||
|  |     int            layers; | ||||||
|  | 
 | ||||||
|  |     /* PRIVATE CONTEXT (set by decoder) */ | ||||||
|  |     const char    *error_msg; | ||||||
|  | 
 | ||||||
|  |     ImagingIncrementalCodec decoder; | ||||||
|  | } JPEG2KDECODESTATE; | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Encoder								*/ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     /* CONFIGURATION */ | ||||||
|  | 
 | ||||||
|  |     /* File descriptor, if available; otherwise, -1 */ | ||||||
|  |     int           fd; | ||||||
|  | 
 | ||||||
|  |     /* Specify the desired format */ | ||||||
|  |     OPJ_CODEC_FORMAT format; | ||||||
|  | 
 | ||||||
|  |     /* Image offset */ | ||||||
|  |     int            offset_x, offset_y; | ||||||
|  | 
 | ||||||
|  |     /* Tile information */ | ||||||
|  |     int            tile_offset_x, tile_offset_y; | ||||||
|  |     int            tile_size_x, tile_size_y; | ||||||
|  | 
 | ||||||
|  |     /* Quality layers (a sequence of numbers giving *either* rates or dB) */ | ||||||
|  |     int            quality_is_in_db; | ||||||
|  |     PyObject      *quality_layers; | ||||||
|  | 
 | ||||||
|  |     /* Number of resolutions (DWT decompositions + 1 */ | ||||||
|  |     int            num_resolutions; | ||||||
|  | 
 | ||||||
|  |     /* Code block size */ | ||||||
|  |     int            cblk_width, cblk_height; | ||||||
|  | 
 | ||||||
|  |     /* Precinct size */ | ||||||
|  |     int            precinct_width, precinct_height; | ||||||
|  | 
 | ||||||
|  |     /* Compression style */ | ||||||
|  |     int            irreversible; | ||||||
|  | 
 | ||||||
|  |     /* Progression order (LRCP/RLCP/RPCL/PCRL/CPRL) */ | ||||||
|  |     OPJ_PROG_ORDER progression; | ||||||
|  | 
 | ||||||
|  |     /* Cinema mode */ | ||||||
|  |     OPJ_CINEMA_MODE cinema_mode; | ||||||
|  | 
 | ||||||
|  |     /* PRIVATE CONTEXT (set by decoder) */ | ||||||
|  |     const char    *error_msg; | ||||||
|  | 
 | ||||||
|  |     ImagingIncrementalCodec encoder; | ||||||
|  | } JPEG2KENCODESTATE; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Local Variables: | ||||||
|  |  * c-basic-offset: 4 | ||||||
|  |  * End: | ||||||
|  |  * | ||||||
|  |  */ | ||||||
							
								
								
									
										753
									
								
								libImaging/Jpeg2KDecode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										753
									
								
								libImaging/Jpeg2KDecode.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,753 @@ | ||||||
|  | /*
 | ||||||
|  |  * The Python Imaging Library. | ||||||
|  |  * $Id$ | ||||||
|  |  * | ||||||
|  |  * decoder for JPEG2000 image data. | ||||||
|  |  * | ||||||
|  |  * history: | ||||||
|  |  * 2014-03-12 ajh  Created | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2014 Coriolis Systems Limited | ||||||
|  |  * Copyright (c) 2014 Alastair Houghton | ||||||
|  |  * | ||||||
|  |  * See the README file for details on usage and redistribution. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "Imaging.h" | ||||||
|  | 
 | ||||||
|  | #ifdef HAVE_OPENJPEG | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include "Jpeg2K.h" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     OPJ_UINT32 tile_index; | ||||||
|  |     OPJ_UINT32 data_size; | ||||||
|  |     OPJ_INT32  x0, y0, x1, y1; | ||||||
|  |     OPJ_UINT32 nb_comps; | ||||||
|  | } JPEG2KTILEINFO; | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Error handler                                                        */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_error(const char *msg, void *client_data) | ||||||
|  | { | ||||||
|  |     JPEG2KDECODESTATE *state = (JPEG2KDECODESTATE *) client_data; | ||||||
|  |     free((void *)state->error_msg); | ||||||
|  |     state->error_msg = strdup(msg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Buffer input stream                                                  */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | static OPJ_SIZE_T | ||||||
|  | j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) | ||||||
|  | { | ||||||
|  |     ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data; | ||||||
|  | 
 | ||||||
|  |     size_t len = ImagingIncrementalCodecRead(decoder, p_buffer, p_nb_bytes); | ||||||
|  | 
 | ||||||
|  |     return len ? len : (OPJ_SIZE_T)-1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static OPJ_OFF_T | ||||||
|  | j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) | ||||||
|  | { | ||||||
|  |     ImagingIncrementalCodec decoder = (ImagingIncrementalCodec)p_user_data; | ||||||
|  |     off_t pos = ImagingIncrementalCodecSkip(decoder, p_nb_bytes); | ||||||
|  | 
 | ||||||
|  |     return pos ? pos : (OPJ_OFF_T)-1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Unpackers                                                            */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | typedef void (*j2k_unpacker_t)(opj_image_t *in, | ||||||
|  |                                const JPEG2KTILEINFO *tileInfo, | ||||||
|  |                                const UINT8 *data,  | ||||||
|  |                                Imaging im); | ||||||
|  | 
 | ||||||
|  | struct j2k_decode_unpacker { | ||||||
|  |     const char          *mode; | ||||||
|  |     OPJ_COLOR_SPACE     color_space; | ||||||
|  |     unsigned            components; | ||||||
|  |     j2k_unpacker_t      unpacker; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static inline | ||||||
|  | unsigned j2ku_shift(unsigned x, int n) | ||||||
|  | { | ||||||
|  |     if (n < 0) | ||||||
|  |         return x >> -n; | ||||||
|  |     else | ||||||
|  |         return x << n; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, | ||||||
|  |             const UINT8 *tiledata, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; | ||||||
|  |     unsigned w = tileinfo->x1 - tileinfo->x0; | ||||||
|  |     unsigned h = tileinfo->y1 - tileinfo->y0; | ||||||
|  | 
 | ||||||
|  |     int shift = 8 - in->comps[0].prec; | ||||||
|  |     int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; | ||||||
|  |     int csiz = (in->comps[0].prec + 7) >> 3; | ||||||
|  | 
 | ||||||
|  |     unsigned x, y; | ||||||
|  | 
 | ||||||
|  |     if (csiz == 3) | ||||||
|  |         csiz = 4; | ||||||
|  | 
 | ||||||
|  |     if (shift < 0) | ||||||
|  |         offset += 1 << (-shift - 1); | ||||||
|  | 
 | ||||||
|  |     switch (csiz) { | ||||||
|  |     case 1: | ||||||
|  |         for (y = 0; y < h; ++y) { | ||||||
|  |             const UINT8 *data = &tiledata[y * w]; | ||||||
|  |             UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; | ||||||
|  |             for (x = 0; x < w; ++x) | ||||||
|  |                 *row++ = j2ku_shift(offset + *data++, shift); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 2: | ||||||
|  |         for (y = 0; y < h; ++y) { | ||||||
|  |             const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w]; | ||||||
|  |             UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; | ||||||
|  |             for (x = 0; x < w; ++x) | ||||||
|  |                 *row++ = j2ku_shift(offset + *data++, shift); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 4: | ||||||
|  |         for (y = 0; y < h; ++y) { | ||||||
|  |             const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w]; | ||||||
|  |             UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; | ||||||
|  |             for (x = 0; x < w; ++x) | ||||||
|  |                 *row++ = j2ku_shift(offset + *data++, shift); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_gray_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, | ||||||
|  |               const UINT8 *tiledata, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; | ||||||
|  |     unsigned w = tileinfo->x1 - tileinfo->x0; | ||||||
|  |     unsigned h = tileinfo->y1 - tileinfo->y0; | ||||||
|  | 
 | ||||||
|  |     int shift = 8 - in->comps[0].prec; | ||||||
|  |     int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; | ||||||
|  |     int csiz = (in->comps[0].prec + 7) >> 3; | ||||||
|  | 
 | ||||||
|  |     unsigned x, y; | ||||||
|  | 
 | ||||||
|  |     if (shift < 0) | ||||||
|  |         offset += 1 << (-shift - 1); | ||||||
|  | 
 | ||||||
|  |     if (csiz == 3) | ||||||
|  |         csiz = 4; | ||||||
|  | 
 | ||||||
|  |     switch (csiz) { | ||||||
|  |     case 1: | ||||||
|  |         for (y = 0; y < h; ++y) { | ||||||
|  |             const UINT8 *data = &tiledata[y * w]; | ||||||
|  |             UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; | ||||||
|  |             for (x = 0; x < w; ++x) { | ||||||
|  |                 UINT8 byte = j2ku_shift(offset + *data++, shift); | ||||||
|  |                 row[0] = row[1] = row[2] = byte; | ||||||
|  |                 row[3] = 0xff; | ||||||
|  |                 row += 4; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 2: | ||||||
|  |         for (y = 0; y < h; ++y) { | ||||||
|  |             const UINT16 *data = (UINT16 *)&tiledata[2 * y * w]; | ||||||
|  |             UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; | ||||||
|  |             for (x = 0; x < w; ++x) { | ||||||
|  |                 UINT8 byte = j2ku_shift(offset + *data++, shift); | ||||||
|  |                 row[0] = row[1] = row[2] = byte; | ||||||
|  |                 row[3] = 0xff; | ||||||
|  |                 row += 4; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 4: | ||||||
|  |         for (y = 0; y < h; ++y) { | ||||||
|  |             const UINT32 *data = (UINT32 *)&tiledata[4 * y * w]; | ||||||
|  |             UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; | ||||||
|  |             for (x = 0; x < w; ++x) { | ||||||
|  |                 UINT8 byte = j2ku_shift(offset + *data++, shift); | ||||||
|  |                 row[0] = row[1] = row[2] = byte; | ||||||
|  |                 row[3] = 0xff; | ||||||
|  |                 row += 4; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_graya_la(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, | ||||||
|  |               const UINT8 *tiledata, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; | ||||||
|  |     unsigned w = tileinfo->x1 - tileinfo->x0; | ||||||
|  |     unsigned h = tileinfo->y1 - tileinfo->y0; | ||||||
|  | 
 | ||||||
|  |     int shift = 8 - in->comps[0].prec; | ||||||
|  |     int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; | ||||||
|  |     int csiz = (in->comps[0].prec + 7) >> 3; | ||||||
|  |     int ashift = 8 - in->comps[1].prec; | ||||||
|  |     int aoffset = in->comps[1].sgnd ? 1 << (in->comps[1].prec - 1) : 0; | ||||||
|  |     int acsiz = (in->comps[1].prec + 7) >> 3; | ||||||
|  |     const UINT8 *atiledata; | ||||||
|  | 
 | ||||||
|  |     unsigned x, y; | ||||||
|  | 
 | ||||||
|  |     if (csiz == 3) | ||||||
|  |         csiz = 4; | ||||||
|  |     if (acsiz == 3) | ||||||
|  |         acsiz = 4; | ||||||
|  | 
 | ||||||
|  |     if (shift < 0) | ||||||
|  |         offset += 1 << (-shift - 1); | ||||||
|  |     if (ashift < 0) | ||||||
|  |         aoffset += 1 << (-ashift - 1); | ||||||
|  | 
 | ||||||
|  |     atiledata = tiledata + csiz * w * h; | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         const UINT8 *data = &tiledata[csiz * y * w]; | ||||||
|  |         const UINT8 *adata = &atiledata[acsiz * y * w]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             UINT32 word = 0, aword = 0, byte; | ||||||
|  | 
 | ||||||
|  |             switch (csiz) { | ||||||
|  |             case 1: word = *data++; break; | ||||||
|  |             case 2: word = *(const UINT16 *)data; data += 2; break; | ||||||
|  |             case 4: word = *(const UINT32 *)data; data += 4; break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch (acsiz) { | ||||||
|  |             case 1: aword = *adata++; break; | ||||||
|  |             case 2: aword = *(const UINT16 *)adata; adata += 2; break; | ||||||
|  |             case 4: aword = *(const UINT32 *)adata; adata += 4; break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             byte = j2ku_shift(offset + word, shift); | ||||||
|  |             row[0] = row[1] = row[2] = byte; | ||||||
|  |             row[3] = j2ku_shift(aoffset + aword, ashift); | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_srgb_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, | ||||||
|  |               const UINT8 *tiledata, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; | ||||||
|  |     unsigned w = tileinfo->x1 - tileinfo->x0; | ||||||
|  |     unsigned h = tileinfo->y1 - tileinfo->y0; | ||||||
|  | 
 | ||||||
|  |     int shifts[3], offsets[3], csiz[3]; | ||||||
|  |     const UINT8 *cdata[3]; | ||||||
|  |     const UINT8 *cptr = tiledata; | ||||||
|  |     unsigned n, x, y; | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < 3; ++n) { | ||||||
|  |         cdata[n] = cptr; | ||||||
|  |         shifts[n] = 8 - in->comps[n].prec; | ||||||
|  |         offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; | ||||||
|  |         csiz[n] = (in->comps[n].prec + 7) >> 3; | ||||||
|  | 
 | ||||||
|  |         if (csiz[n] == 3) | ||||||
|  |             csiz[n] = 4; | ||||||
|  | 
 | ||||||
|  |         if (shifts[n] < 0) | ||||||
|  |             offsets[n] += 1 << (-shifts[n] - 1); | ||||||
|  | 
 | ||||||
|  |         cptr += csiz[n] * w * h; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         const UINT8 *data[3]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         for (n = 0; n < 3; ++n) | ||||||
|  |             data[n] = &cdata[n][csiz[n] * y * w]; | ||||||
|  |          | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             for (n = 0; n < 3; ++n) { | ||||||
|  |                 UINT32 word = 0; | ||||||
|  | 
 | ||||||
|  |                 switch (csiz[n]) { | ||||||
|  |                 case 1: word = *data[n]++; break; | ||||||
|  |                 case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break; | ||||||
|  |                 case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 row[n] = j2ku_shift(offsets[n] + word, shifts[n]); | ||||||
|  |             } | ||||||
|  |             row[3] = 0xff; | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_sycc_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, | ||||||
|  |               const UINT8 *tiledata, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; | ||||||
|  |     unsigned w = tileinfo->x1 - tileinfo->x0; | ||||||
|  |     unsigned h = tileinfo->y1 - tileinfo->y0; | ||||||
|  | 
 | ||||||
|  |     int shifts[3], offsets[3], csiz[3]; | ||||||
|  |     const UINT8 *cdata[3]; | ||||||
|  |     const UINT8 *cptr = tiledata; | ||||||
|  |     unsigned n, x, y; | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < 3; ++n) { | ||||||
|  |         cdata[n] = cptr; | ||||||
|  |         shifts[n] = 8 - in->comps[n].prec; | ||||||
|  |         offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; | ||||||
|  |         csiz[n] = (in->comps[n].prec + 7) >> 3; | ||||||
|  | 
 | ||||||
|  |         if (csiz[n] == 3) | ||||||
|  |             csiz[n] = 4; | ||||||
|  | 
 | ||||||
|  |         if (shifts[n] < 0) | ||||||
|  |             offsets[n] += 1 << (-shifts[n] - 1); | ||||||
|  | 
 | ||||||
|  |         cptr += csiz[n] * w * h; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         const UINT8 *data[3]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         UINT8 *row_start = row; | ||||||
|  |         for (n = 0; n < 3; ++n) | ||||||
|  |             data[n] = &cdata[n][csiz[n] * y * w]; | ||||||
|  |          | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             for (n = 0; n < 3; ++n) { | ||||||
|  |                 UINT32 word = 0; | ||||||
|  | 
 | ||||||
|  |                 switch (csiz[n]) { | ||||||
|  |                 case 1: word = *data[n]++; break; | ||||||
|  |                 case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break; | ||||||
|  |                 case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 row[n] = j2ku_shift(offsets[n] + word, shifts[n]); | ||||||
|  |             } | ||||||
|  |             row[3] = 0xff; | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ImagingConvertYCbCr2RGB(row_start, row_start, w); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, | ||||||
|  |                 const UINT8 *tiledata, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; | ||||||
|  |     unsigned w = tileinfo->x1 - tileinfo->x0; | ||||||
|  |     unsigned h = tileinfo->y1 - tileinfo->y0; | ||||||
|  | 
 | ||||||
|  |     int shifts[4], offsets[4], csiz[4]; | ||||||
|  |     const UINT8 *cdata[4]; | ||||||
|  |     const UINT8 *cptr = tiledata; | ||||||
|  |     unsigned n, x, y; | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < 4; ++n) { | ||||||
|  |         cdata[n] = cptr; | ||||||
|  |         shifts[n] = 8 - in->comps[n].prec; | ||||||
|  |         offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; | ||||||
|  |         csiz[n] = (in->comps[n].prec + 7) >> 3; | ||||||
|  | 
 | ||||||
|  |         if (csiz[n] == 3) | ||||||
|  |             csiz[n] = 4; | ||||||
|  | 
 | ||||||
|  |         if (shifts[n] < 0) | ||||||
|  |             offsets[n] += 1 << (-shifts[n] - 1); | ||||||
|  | 
 | ||||||
|  |         cptr += csiz[n] * w * h; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         const UINT8 *data[4]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         for (n = 0; n < 4; ++n) | ||||||
|  |             data[n] = &cdata[n][csiz[n] * y * w]; | ||||||
|  |          | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             for (n = 0; n < 4; ++n) { | ||||||
|  |                 UINT32 word = 0; | ||||||
|  | 
 | ||||||
|  |                 switch (csiz[n]) { | ||||||
|  |                 case 1: word = *data[n]++; break; | ||||||
|  |                 case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break; | ||||||
|  |                 case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 row[n] = j2ku_shift(offsets[n] + word, shifts[n]); | ||||||
|  |             } | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, | ||||||
|  |                 const UINT8 *tiledata, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; | ||||||
|  |     unsigned w = tileinfo->x1 - tileinfo->x0; | ||||||
|  |     unsigned h = tileinfo->y1 - tileinfo->y0; | ||||||
|  | 
 | ||||||
|  |     int shifts[4], offsets[4], csiz[4]; | ||||||
|  |     const UINT8 *cdata[4]; | ||||||
|  |     const UINT8 *cptr = tiledata; | ||||||
|  |     unsigned n, x, y; | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < 4; ++n) { | ||||||
|  |         cdata[n] = cptr; | ||||||
|  |         shifts[n] = 8 - in->comps[n].prec; | ||||||
|  |         offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; | ||||||
|  |         csiz[n] = (in->comps[n].prec + 7) >> 3; | ||||||
|  | 
 | ||||||
|  |         if (csiz[n] == 3) | ||||||
|  |             csiz[n] = 4; | ||||||
|  | 
 | ||||||
|  |         if (shifts[n] < 0) | ||||||
|  |             offsets[n] += 1 << (-shifts[n] - 1); | ||||||
|  | 
 | ||||||
|  |         cptr += csiz[n] * w * h; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         const UINT8 *data[4]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         UINT8 *row_start = row; | ||||||
|  |         for (n = 0; n < 4; ++n) | ||||||
|  |             data[n] = &cdata[n][csiz[n] * y * w]; | ||||||
|  |          | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             for (n = 0; n < 4; ++n) { | ||||||
|  |                 UINT32 word = 0; | ||||||
|  | 
 | ||||||
|  |                 switch (csiz[n]) { | ||||||
|  |                 case 1: word = *data[n]++; break; | ||||||
|  |                 case 2: word = *(const UINT16 *)data[n]; data[n] += 2; break; | ||||||
|  |                 case 4: word = *(const UINT32 *)data[n]; data[n] += 4; break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 row[n] = j2ku_shift(offsets[n] + word, shifts[n]); | ||||||
|  |             } | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ImagingConvertYCbCr2RGB(row_start, row_start, w); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct j2k_decode_unpacker j2k_unpackers[] = { | ||||||
|  |     { "L", OPJ_CLRSPC_GRAY, 1, j2ku_gray_l }, | ||||||
|  |     { "LA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la }, | ||||||
|  |     { "RGB", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb }, | ||||||
|  |     { "RGB", OPJ_CLRSPC_GRAY, 2, j2ku_gray_rgb }, | ||||||
|  |     { "RGB", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb }, | ||||||
|  |     { "RGB", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb }, | ||||||
|  |     { "RGB", OPJ_CLRSPC_SRGB, 4, j2ku_srgb_rgb }, | ||||||
|  |     { "RGB", OPJ_CLRSPC_SYCC, 4, j2ku_sycc_rgb }, | ||||||
|  |     { "RGBA", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb }, | ||||||
|  |     { "RGBA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la }, | ||||||
|  |     { "RGBA", OPJ_CLRSPC_SRGB, 3, j2ku_srgb_rgb }, | ||||||
|  |     { "RGBA", OPJ_CLRSPC_SYCC, 3, j2ku_sycc_rgb }, | ||||||
|  |     { "RGBA", OPJ_CLRSPC_SRGB, 4, j2ku_srgba_rgba }, | ||||||
|  |     { "RGBA", OPJ_CLRSPC_SYCC, 4, j2ku_sycca_rgba }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Decoder                                                              */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     J2K_STATE_START = 0, | ||||||
|  |     J2K_STATE_DECODING = 1, | ||||||
|  |     J2K_STATE_DONE = 2, | ||||||
|  |     J2K_STATE_FAILED = 3, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | j2k_decode_entry(Imaging im, ImagingCodecState state, | ||||||
|  |                  ImagingIncrementalCodec decoder) | ||||||
|  | { | ||||||
|  |     JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context; | ||||||
|  |     opj_stream_t *stream = NULL; | ||||||
|  |     opj_image_t *image = NULL; | ||||||
|  |     opj_codec_t *codec = NULL; | ||||||
|  |     opj_dparameters_t params; | ||||||
|  |     OPJ_COLOR_SPACE color_space; | ||||||
|  |     j2k_unpacker_t unpack = NULL; | ||||||
|  |     size_t buffer_size = 0; | ||||||
|  |     unsigned n; | ||||||
|  | 
 | ||||||
|  |     stream = opj_stream_default_create(OPJ_TRUE); | ||||||
|  | 
 | ||||||
|  |     if (!stream) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     opj_stream_set_read_function(stream, j2k_read); | ||||||
|  |     opj_stream_set_skip_function(stream, j2k_skip); | ||||||
|  | 
 | ||||||
|  |     opj_stream_set_user_data(stream, decoder); | ||||||
|  | 
 | ||||||
|  |     /* Setup decompression context */ | ||||||
|  |     context->error_msg = NULL; | ||||||
|  |      | ||||||
|  |     opj_set_default_decoder_parameters(¶ms); | ||||||
|  |     params.cp_reduce = context->reduce; | ||||||
|  |     params.cp_layer = context->layers; | ||||||
|  |      | ||||||
|  |     codec = opj_create_decompress(context->format); | ||||||
|  |      | ||||||
|  |     if (!codec) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     opj_set_error_handler(codec, j2k_error, context); | ||||||
|  |     opj_setup_decoder(codec, ¶ms); | ||||||
|  | 
 | ||||||
|  |     if (!opj_read_header(stream, codec, &image)) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Check that this image is something we can handle */ | ||||||
|  |     if (image->numcomps < 1 || image->numcomps > 4 | ||||||
|  |         || image->color_space == OPJ_CLRSPC_UNKNOWN) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     for (n = 1; n < image->numcomps; ++n) { | ||||||
|  |         if (image->comps[n].dx != 1 || image->comps[n].dy != 1) { | ||||||
|  |             state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |             state->state = J2K_STATE_FAILED; | ||||||
|  |             goto quick_exit; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /* 
 | ||||||
|  |          Colorspace    Number of components    PIL mode | ||||||
|  |        ------------------------------------------------------ | ||||||
|  |          sRGB          3                       RGB | ||||||
|  |          sRGB          4                       RGBA | ||||||
|  |          gray          1                       L or I | ||||||
|  |          gray          2                       LA | ||||||
|  |          YCC           3                       YCbCr | ||||||
|  |         | ||||||
|  |         | ||||||
|  |        If colorspace is unspecified, we assume: | ||||||
|  |         | ||||||
|  |            Number of components   Colorspace | ||||||
|  |          ----------------------------------------- | ||||||
|  |            1                      gray | ||||||
|  |            2                      gray (+ alpha) | ||||||
|  |            3                      sRGB | ||||||
|  |            4                      sRGB (+ alpha) | ||||||
|  |         | ||||||
|  |     */ | ||||||
|  |      | ||||||
|  |     /* Find the correct unpacker */ | ||||||
|  |     color_space = image->color_space; | ||||||
|  |      | ||||||
|  |     if (color_space == OPJ_CLRSPC_UNSPECIFIED) { | ||||||
|  |         switch (image->numcomps) { | ||||||
|  |         case 1: case 2: color_space = OPJ_CLRSPC_GRAY; break; | ||||||
|  |         case 3: case 4: color_space = OPJ_CLRSPC_SRGB; break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < sizeof(j2k_unpackers) / sizeof (j2k_unpackers[0]); ++n) { | ||||||
|  |         if (color_space == j2k_unpackers[n].color_space | ||||||
|  |             && image->numcomps == j2k_unpackers[n].components | ||||||
|  |             && strcmp (im->mode, j2k_unpackers[n].mode) == 0) { | ||||||
|  |             unpack = j2k_unpackers[n].unpacker; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!unpack) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit;  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Decode the image tile-by-tile; this means we only need use as much
 | ||||||
|  |        memory as is required for one tile's worth of components. */ | ||||||
|  |     for (;;) { | ||||||
|  |         JPEG2KTILEINFO tile_info; | ||||||
|  |         OPJ_BOOL should_continue; | ||||||
|  |         unsigned correction = (1 << params.cp_reduce) - 1; | ||||||
|  | 
 | ||||||
|  |         if (!opj_read_tile_header(codec, | ||||||
|  |                                   stream, | ||||||
|  |                                   &tile_info.tile_index, | ||||||
|  |                                   &tile_info.data_size, | ||||||
|  |                                   &tile_info.x0, &tile_info.y0, | ||||||
|  |                                   &tile_info.x1, &tile_info.y1, | ||||||
|  |                                   &tile_info.nb_comps, | ||||||
|  |                                   &should_continue)) { | ||||||
|  |             state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |             state->state = J2K_STATE_FAILED; | ||||||
|  |             goto quick_exit; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!should_continue) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         /* Adjust the tile co-ordinates based on the reduction (OpenJPEG
 | ||||||
|  |            doesn't do this for us) */ | ||||||
|  |         tile_info.x0 = (tile_info.x0 + correction) >> context->reduce; | ||||||
|  |         tile_info.y0 = (tile_info.y0 + correction) >> context->reduce; | ||||||
|  |         tile_info.x1 = (tile_info.x1 + correction) >> context->reduce; | ||||||
|  |         tile_info.y1 = (tile_info.y1 + correction) >> context->reduce; | ||||||
|  | 
 | ||||||
|  |         if (buffer_size < tile_info.data_size) { | ||||||
|  |             UINT8 *new = realloc (state->buffer, tile_info.data_size); | ||||||
|  |             if (!new) { | ||||||
|  |                 state->errcode = IMAGING_CODEC_MEMORY; | ||||||
|  |                 state->state = J2K_STATE_FAILED; | ||||||
|  |                 goto quick_exit; | ||||||
|  |             } | ||||||
|  |             state->buffer = new; | ||||||
|  |             buffer_size = tile_info.data_size; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!opj_decode_tile_data(codec, | ||||||
|  |                                   tile_info.tile_index, | ||||||
|  |                                   (OPJ_BYTE *)state->buffer, | ||||||
|  |                                   tile_info.data_size, | ||||||
|  |                                   stream)) { | ||||||
|  |             state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |             state->state = J2K_STATE_FAILED; | ||||||
|  |             goto quick_exit; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* Check the tile bounds; if the tile is outside the image area,
 | ||||||
|  |            or if it has a negative width or height (i.e. the coordinates are | ||||||
|  |            swapped), bail. */ | ||||||
|  |         if (tile_info.x0 >= tile_info.x1 | ||||||
|  |             || tile_info.y0 >= tile_info.y1 | ||||||
|  |             || tile_info.x0 < image->x0 | ||||||
|  |             || tile_info.y0 < image->y0 | ||||||
|  |             || tile_info.x1 - image->x0 > im->xsize | ||||||
|  |             || tile_info.y1 - image->y0 > im->ysize) { | ||||||
|  |             state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |             state->state = J2K_STATE_FAILED; | ||||||
|  |             goto quick_exit; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         unpack(image, &tile_info, state->buffer, im); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!opj_end_decompress(codec, stream)) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     state->state = J2K_STATE_DONE; | ||||||
|  |     state->errcode = IMAGING_CODEC_END; | ||||||
|  | 
 | ||||||
|  |  quick_exit: | ||||||
|  |     if (codec) | ||||||
|  |         opj_destroy_codec(codec); | ||||||
|  |     if (image) | ||||||
|  |         opj_image_destroy(image); | ||||||
|  |     if (stream) | ||||||
|  |         opj_stream_destroy(stream); | ||||||
|  | 
 | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) | ||||||
|  | { | ||||||
|  |     JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context; | ||||||
|  | 
 | ||||||
|  |     if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED) | ||||||
|  |         return -1; | ||||||
|  | 
 | ||||||
|  |     if (state->state == J2K_STATE_START) { | ||||||
|  |         context->decoder = ImagingIncrementalCodecCreate(j2k_decode_entry, | ||||||
|  |                                                          im, state, | ||||||
|  |                                                          INCREMENTAL_CODEC_READ, | ||||||
|  |                                                          INCREMENTAL_CODEC_NOT_SEEKABLE, | ||||||
|  |                                                          context->fd); | ||||||
|  | 
 | ||||||
|  |         if (!context->decoder) { | ||||||
|  |             state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |             state->state = J2K_STATE_FAILED; | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state->state = J2K_STATE_DECODING; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ImagingIncrementalCodecPushBuffer(context->decoder, buf, bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Cleanup                                                              */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | ImagingJpeg2KDecodeCleanup(ImagingCodecState state) { | ||||||
|  |     JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *)state->context; | ||||||
|  | 
 | ||||||
|  |     if (context->error_msg) | ||||||
|  |         free ((void *)context->error_msg); | ||||||
|  | 
 | ||||||
|  |     if (context->decoder) | ||||||
|  |         ImagingIncrementalCodecDestroy(context->decoder); | ||||||
|  | 
 | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char * | ||||||
|  | ImagingJpeg2KVersion(void) | ||||||
|  | { | ||||||
|  |     return opj_version(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif /* HAVE_OPENJPEG */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Local Variables: | ||||||
|  |  * c-basic-offset: 4 | ||||||
|  |  * End: | ||||||
|  |  * | ||||||
|  |  */ | ||||||
							
								
								
									
										562
									
								
								libImaging/Jpeg2KEncode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										562
									
								
								libImaging/Jpeg2KEncode.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,562 @@ | ||||||
|  | /*
 | ||||||
|  |  * The Python Imaging Library. | ||||||
|  |  * $Id$ | ||||||
|  |  * | ||||||
|  |  * decoder for JPEG2000 image data. | ||||||
|  |  * | ||||||
|  |  * history: | ||||||
|  |  * 2014-03-12 ajh  Created | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2014 Coriolis Systems Limited | ||||||
|  |  * Copyright (c) 2014 Alastair Houghton | ||||||
|  |  * | ||||||
|  |  * See the README file for details on usage and redistribution. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "Imaging.h" | ||||||
|  | 
 | ||||||
|  | #ifdef HAVE_OPENJPEG | ||||||
|  | 
 | ||||||
|  | #include "Jpeg2K.h" | ||||||
|  | 
 | ||||||
|  | #define CINEMA_24_CS_LENGTH   1302083 | ||||||
|  | #define CINEMA_48_CS_LENGTH    651041 | ||||||
|  | #define COMP_24_CS_MAX_LENGTH 1041666 | ||||||
|  | #define COMP_48_CS_MAX_LENGTH  520833 | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Error handler                                                        */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_error(const char *msg, void *client_data) | ||||||
|  | { | ||||||
|  |     JPEG2KENCODESTATE *state = (JPEG2KENCODESTATE *) client_data; | ||||||
|  |     free((void *)state->error_msg); | ||||||
|  |     state->error_msg = strdup(msg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Buffer output stream                                                 */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | static OPJ_SIZE_T | ||||||
|  | j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) | ||||||
|  | { | ||||||
|  |     ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data; | ||||||
|  |     size_t len = ImagingIncrementalCodecWrite(encoder, p_buffer, p_nb_bytes); | ||||||
|  | 
 | ||||||
|  |     return len ? len : (OPJ_SIZE_T)-1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static OPJ_OFF_T | ||||||
|  | j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) | ||||||
|  | { | ||||||
|  |     ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data; | ||||||
|  |     off_t pos = ImagingIncrementalCodecSkip(encoder, p_nb_bytes); | ||||||
|  | 
 | ||||||
|  |     return pos ? pos : (OPJ_OFF_T)-1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static OPJ_BOOL | ||||||
|  | j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) | ||||||
|  | { | ||||||
|  |     ImagingIncrementalCodec encoder = (ImagingIncrementalCodec)p_user_data; | ||||||
|  |     off_t pos = ImagingIncrementalCodecSeek(encoder, p_nb_bytes); | ||||||
|  | 
 | ||||||
|  |     return pos == p_nb_bytes; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Encoder                                                              */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | typedef void (*j2k_pack_tile_t)(Imaging im, UINT8 *buf, | ||||||
|  |                                 unsigned x0, unsigned y0, | ||||||
|  |                                 unsigned w, unsigned h); | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_pack_l(Imaging im, UINT8 *buf, | ||||||
|  |            unsigned x0, unsigned y0, unsigned w, unsigned h) | ||||||
|  | { | ||||||
|  |     UINT8 *ptr = buf; | ||||||
|  |     unsigned x,y; | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); | ||||||
|  |         for (x = 0; x < w; ++x) | ||||||
|  |             *ptr++ = *data++; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_pack_la(Imaging im, UINT8 *buf, | ||||||
|  |             unsigned x0, unsigned y0, unsigned w, unsigned h) | ||||||
|  | { | ||||||
|  |     UINT8 *ptr = buf; | ||||||
|  |     UINT8 *ptra = buf + w * h; | ||||||
|  |     unsigned x,y; | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             *ptr++ = data[0]; | ||||||
|  |             *ptra++ = data[3]; | ||||||
|  |             data += 4; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_pack_rgb(Imaging im, UINT8 *buf, | ||||||
|  |              unsigned x0, unsigned y0, unsigned w, unsigned h) | ||||||
|  | { | ||||||
|  |     UINT8 *pr = buf; | ||||||
|  |     UINT8 *pg = pr + w * h; | ||||||
|  |     UINT8 *pb = pg + w * h; | ||||||
|  |     unsigned x,y; | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             *pr++ = data[0]; | ||||||
|  |             *pg++ = data[1]; | ||||||
|  |             *pb++ = data[2]; | ||||||
|  |             data += 4; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_pack_rgba(Imaging im, UINT8 *buf, | ||||||
|  |               unsigned x0, unsigned y0, unsigned w, unsigned h) | ||||||
|  | { | ||||||
|  |     UINT8 *pr = buf; | ||||||
|  |     UINT8 *pg = pr + w * h; | ||||||
|  |     UINT8 *pb = pg + w * h; | ||||||
|  |     UINT8 *pa = pb + w * h; | ||||||
|  |     unsigned x,y; | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         UINT8 *data = (UINT8 *)(im->image[y + y0] + 4 * x0); | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             *pr++ = *data++; | ||||||
|  |             *pg++ = *data++; | ||||||
|  |             *pb++ = *data++; | ||||||
|  |             *pa++ = *data++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     J2K_STATE_START = 0, | ||||||
|  |     J2K_STATE_ENCODING = 1, | ||||||
|  |     J2K_STATE_DONE = 2, | ||||||
|  |     J2K_STATE_FAILED = 3, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) | ||||||
|  | { | ||||||
|  |     float rate; | ||||||
|  |     unsigned n; | ||||||
|  | 
 | ||||||
|  |     /* These settings have been copied from opj_compress in the OpenJPEG
 | ||||||
|  |        sources. */ | ||||||
|  | 
 | ||||||
|  |     params->tile_size_on = OPJ_FALSE; | ||||||
|  |     params->cp_tdx = params->cp_tdy = 1; | ||||||
|  |     params->tp_flag = 'C'; | ||||||
|  |     params->tp_on = 1; | ||||||
|  |     params->cp_tx0 = params->cp_ty0 = 0; | ||||||
|  |     params->image_offset_x0 = params->image_offset_y0 = 0; | ||||||
|  |     params->cblockw_init = 32; | ||||||
|  |     params->cblockh_init = 32; | ||||||
|  |     params->csty |= 0x01; | ||||||
|  |     params->prog_order = OPJ_CPRL; | ||||||
|  |     params->roi_compno = -1; | ||||||
|  |     params->subsampling_dx = params->subsampling_dy = 1; | ||||||
|  |     params->irreversible = 1; | ||||||
|  | 
 | ||||||
|  |     if (params->cp_cinema == OPJ_CINEMA4K_24) { | ||||||
|  |         float max_rate = ((float)(components * im->xsize * im->ysize * 8) | ||||||
|  |                           / (CINEMA_24_CS_LENGTH * 8)); | ||||||
|  | 
 | ||||||
|  |         params->POC[0].tile = 1; | ||||||
|  |         params->POC[0].resno0 = 0; | ||||||
|  |         params->POC[0].compno0 = 0; | ||||||
|  |         params->POC[0].layno1 = 1; | ||||||
|  |         params->POC[0].resno1 = params->numresolution - 1; | ||||||
|  |         params->POC[0].compno1 = 3; | ||||||
|  |         params->POC[0].prg1 = OPJ_CPRL; | ||||||
|  |         params->POC[1].tile = 1; | ||||||
|  |         params->POC[1].resno0 = 0; | ||||||
|  |         params->POC[1].compno0 = 0; | ||||||
|  |         params->POC[1].layno1 = 1; | ||||||
|  |         params->POC[1].resno1 = params->numresolution - 1; | ||||||
|  |         params->POC[1].compno1 = 3; | ||||||
|  |         params->POC[1].prg1 = OPJ_CPRL; | ||||||
|  |         params->numpocs = 2; | ||||||
|  | 
 | ||||||
|  |         for (n = 0; n < params->tcp_numlayers; ++n) { | ||||||
|  |             rate = 0; | ||||||
|  |             if (params->tcp_rates[0] == 0) { | ||||||
|  |                 params->tcp_rates[n] = max_rate; | ||||||
|  |             } else { | ||||||
|  |                 rate = ((float)(components * im->xsize * im->ysize * 8) | ||||||
|  |                         / (params->tcp_rates[n] * 8)); | ||||||
|  |                 if (rate > CINEMA_24_CS_LENGTH) | ||||||
|  |                     params->tcp_rates[n] = max_rate; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         params->max_comp_size = COMP_24_CS_MAX_LENGTH; | ||||||
|  |     } else { | ||||||
|  |         float max_rate = ((float)(components * im->xsize * im->ysize * 8) | ||||||
|  |                           / (CINEMA_48_CS_LENGTH * 8)); | ||||||
|  | 
 | ||||||
|  |         for (n = 0; n < params->tcp_numlayers; ++n) { | ||||||
|  |             rate = 0; | ||||||
|  |             if (params->tcp_rates[0] == 0) { | ||||||
|  |                 params->tcp_rates[n] = max_rate; | ||||||
|  |             } else { | ||||||
|  |                 rate = ((float)(components * im->xsize * im->ysize * 8) | ||||||
|  |                         / (params->tcp_rates[n] * 8)); | ||||||
|  |                 if (rate > CINEMA_48_CS_LENGTH) | ||||||
|  |                     params->tcp_rates[n] = max_rate; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         params->max_comp_size = COMP_48_CS_MAX_LENGTH; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | j2k_encode_entry(Imaging im, ImagingCodecState state, | ||||||
|  |                  ImagingIncrementalCodec encoder) | ||||||
|  | { | ||||||
|  |     JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; | ||||||
|  |     opj_stream_t *stream = NULL; | ||||||
|  |     opj_image_t *image = NULL; | ||||||
|  |     opj_codec_t *codec = NULL; | ||||||
|  |     opj_cparameters_t params; | ||||||
|  |     unsigned components; | ||||||
|  |     OPJ_COLOR_SPACE color_space; | ||||||
|  |     opj_image_cmptparm_t image_params[4]; | ||||||
|  |     unsigned xsiz, ysiz; | ||||||
|  |     unsigned tile_width, tile_height; | ||||||
|  |     unsigned tiles_x, tiles_y, num_tiles; | ||||||
|  |     unsigned x, y, tile_ndx; | ||||||
|  |     unsigned n; | ||||||
|  |     j2k_pack_tile_t pack; | ||||||
|  |     int ret = -1; | ||||||
|  | 
 | ||||||
|  |     stream = opj_stream_default_create(OPJ_FALSE); | ||||||
|  | 
 | ||||||
|  |     if (!stream) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     opj_stream_set_write_function(stream, j2k_write); | ||||||
|  |     opj_stream_set_skip_function(stream, j2k_skip); | ||||||
|  |     opj_stream_set_seek_function(stream, j2k_seek); | ||||||
|  | 
 | ||||||
|  |     opj_stream_set_user_data(stream, encoder); | ||||||
|  | 
 | ||||||
|  |     /* Setup an opj_image */ | ||||||
|  |     if (strcmp (im->mode, "L") == 0) { | ||||||
|  |         components = 1; | ||||||
|  |         color_space = OPJ_CLRSPC_GRAY; | ||||||
|  |         pack = j2k_pack_l; | ||||||
|  |     } else if (strcmp (im->mode, "LA") == 0) { | ||||||
|  |         components = 2;  | ||||||
|  |         color_space = OPJ_CLRSPC_GRAY; | ||||||
|  |         pack = j2k_pack_la; | ||||||
|  |     } else if (strcmp (im->mode, "RGB") == 0) { | ||||||
|  |         components = 3; | ||||||
|  |         color_space = OPJ_CLRSPC_SRGB; | ||||||
|  |         pack = j2k_pack_rgb; | ||||||
|  |     } else if (strcmp (im->mode, "YCbCr") == 0) { | ||||||
|  |         components = 3; | ||||||
|  |         color_space = OPJ_CLRSPC_SYCC; | ||||||
|  |         pack = j2k_pack_rgb; | ||||||
|  |     } else if (strcmp (im->mode, "RGBA") == 0) { | ||||||
|  |         components = 4; | ||||||
|  |         color_space = OPJ_CLRSPC_SRGB; | ||||||
|  |         pack = j2k_pack_rgba; | ||||||
|  |     } else { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < components; ++n) { | ||||||
|  |         image_params[n].dx = image_params[n].dy = 1; | ||||||
|  |         image_params[n].w = im->xsize; | ||||||
|  |         image_params[n].h = im->ysize; | ||||||
|  |         image_params[n].x0 = image_params[n].y0 = 0; | ||||||
|  |         image_params[n].prec = 8; | ||||||
|  |         image_params[n].bpp = 8; | ||||||
|  |         image_params[n].sgnd = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     image = opj_image_create(components, image_params, color_space); | ||||||
|  | 
 | ||||||
|  |     /* Setup compression context */ | ||||||
|  |     context->error_msg = NULL; | ||||||
|  | 
 | ||||||
|  |     opj_set_default_encoder_parameters(¶ms); | ||||||
|  |      | ||||||
|  |     params.image_offset_x0 = context->offset_x; | ||||||
|  |     params.image_offset_y0 = context->offset_y; | ||||||
|  | 
 | ||||||
|  |     if (context->tile_size_x && context->tile_size_y) { | ||||||
|  |         params.tile_size_on = OPJ_TRUE; | ||||||
|  |         params.cp_tx0 = context->tile_offset_x; | ||||||
|  |         params.cp_ty0 = context->tile_offset_y; | ||||||
|  |         params.cp_tdx = context->tile_size_x; | ||||||
|  |         params.cp_tdy = context->tile_size_y; | ||||||
|  | 
 | ||||||
|  |         tile_width = params.cp_tdx; | ||||||
|  |         tile_height = params.cp_tdy; | ||||||
|  |     } else { | ||||||
|  |         params.cp_tx0 = 0; | ||||||
|  |         params.cp_ty0 = 0; | ||||||
|  |         params.cp_tdx = 1; | ||||||
|  |         params.cp_tdy = 1; | ||||||
|  | 
 | ||||||
|  |         tile_width = im->xsize; | ||||||
|  |         tile_height = im->ysize; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (context->quality_layers && PySequence_Check(context->quality_layers)) { | ||||||
|  |         Py_ssize_t len = PySequence_Length(context->quality_layers); | ||||||
|  |         Py_ssize_t n; | ||||||
|  |         float *pq; | ||||||
|  | 
 | ||||||
|  |         if (len) { | ||||||
|  |             if (len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) | ||||||
|  |                 len = sizeof(params.tcp_rates)/sizeof(params.tcp_rates[0]); | ||||||
|  | 
 | ||||||
|  |             params.tcp_numlayers = (int)len; | ||||||
|  | 
 | ||||||
|  |             if (context->quality_is_in_db) { | ||||||
|  |                 params.cp_disto_alloc = params.cp_fixed_alloc = 0; | ||||||
|  |                 params.cp_fixed_quality = 1; | ||||||
|  |                 pq = params.tcp_distoratio; | ||||||
|  |             } else { | ||||||
|  |                 params.cp_disto_alloc = 1; | ||||||
|  |                 params.cp_fixed_alloc = params.cp_fixed_quality = 0; | ||||||
|  |                 pq = params.tcp_rates; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             for (n = 0; n < len; ++n) { | ||||||
|  |                 PyObject *obj = PySequence_ITEM(context->quality_layers, n); | ||||||
|  |                 pq[n] = PyFloat_AsDouble(obj); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         params.tcp_numlayers = 1; | ||||||
|  |         params.tcp_rates[0] = 0; | ||||||
|  |         params.cp_disto_alloc = 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (context->num_resolutions) | ||||||
|  |         params.numresolution = context->num_resolutions; | ||||||
|  | 
 | ||||||
|  |     if (context->cblk_width >= 4 && context->cblk_width <= 1024 | ||||||
|  |         && context->cblk_height >= 4 && context->cblk_height <= 1024 | ||||||
|  |         && context->cblk_width * context->cblk_height <= 4096) { | ||||||
|  |         params.cblockw_init = context->cblk_width; | ||||||
|  |         params.cblockh_init = context->cblk_height; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (context->precinct_width >= 4 && context->precinct_height >= 4 | ||||||
|  |         && context->precinct_width >= context->cblk_width | ||||||
|  |         && context->precinct_height > context->cblk_height) { | ||||||
|  |         params.prcw_init[0] = context->precinct_width; | ||||||
|  |         params.prch_init[0] = context->precinct_height; | ||||||
|  |         params.res_spec = 1; | ||||||
|  |         params.csty |= 0x01; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     params.irreversible = context->irreversible; | ||||||
|  | 
 | ||||||
|  |     params.prog_order = context->progression; | ||||||
|  | 
 | ||||||
|  |     params.cp_cinema = context->cinema_mode; | ||||||
|  | 
 | ||||||
|  |     switch (params.cp_cinema) { | ||||||
|  |     case OPJ_OFF: | ||||||
|  |         params.cp_rsiz = OPJ_STD_RSIZ; | ||||||
|  |         break; | ||||||
|  |     case OPJ_CINEMA2K_24: | ||||||
|  |     case OPJ_CINEMA2K_48: | ||||||
|  |         params.cp_rsiz = OPJ_CINEMA2K; | ||||||
|  |         if (params.numresolution > 6) | ||||||
|  |             params.numresolution = 6; | ||||||
|  |         break; | ||||||
|  |     case OPJ_CINEMA4K_24: | ||||||
|  |         params.cp_rsiz = OPJ_CINEMA4K; | ||||||
|  |         if (params.numresolution > 7) | ||||||
|  |             params.numresolution = 7; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (context->cinema_mode != OPJ_OFF) | ||||||
|  |         j2k_set_cinema_params(im, components, ¶ms); | ||||||
|  | 
 | ||||||
|  |     /* Set up the reference grid in the image */ | ||||||
|  |     image->x0 = params.image_offset_x0; | ||||||
|  |     image->y0 = params.image_offset_y0; | ||||||
|  |     image->x1 = xsiz = im->xsize + params.image_offset_x0; | ||||||
|  |     image->y1 = ysiz = im->ysize + params.image_offset_y0; | ||||||
|  | 
 | ||||||
|  |     /* Create the compressor */ | ||||||
|  |     codec = opj_create_compress(context->format); | ||||||
|  | 
 | ||||||
|  |     if (!codec) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     opj_set_error_handler(codec, j2k_error, context); | ||||||
|  |     opj_setup_encoder(codec, ¶ms, image); | ||||||
|  | 
 | ||||||
|  |     /* Start encoding */ | ||||||
|  |     if (!opj_start_compress(codec, image, stream)) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Write each tile */ | ||||||
|  |     tiles_x = (im->xsize + (params.image_offset_x0 - params.cp_tx0) | ||||||
|  |                + tile_width - 1) / tile_width; | ||||||
|  |     tiles_y = (im->ysize + (params.image_offset_y0 - params.cp_ty0) | ||||||
|  |                + tile_height - 1) / tile_height; | ||||||
|  | 
 | ||||||
|  |     num_tiles = tiles_x * tiles_y; | ||||||
|  | 
 | ||||||
|  |     state->buffer = malloc (tile_width * tile_height * components); | ||||||
|  | 
 | ||||||
|  |     tile_ndx = 0; | ||||||
|  |     for (y = 0; y < tiles_y; ++y) { | ||||||
|  |         unsigned ty0 = params.cp_ty0 + y * tile_height; | ||||||
|  |         unsigned ty1 = ty0 + tile_height; | ||||||
|  |         unsigned pixy, pixh; | ||||||
|  | 
 | ||||||
|  |         if (ty0 < params.image_offset_y0) | ||||||
|  |             ty0 = params.image_offset_y0; | ||||||
|  |         if (ty1 > ysiz) | ||||||
|  |             ty1 = ysiz; | ||||||
|  | 
 | ||||||
|  |         pixy = ty0 - params.image_offset_y0; | ||||||
|  |         pixh = ty1 - ty0; | ||||||
|  | 
 | ||||||
|  |         for (x = 0; x < tiles_x; ++x) { | ||||||
|  |             unsigned tx0 = params.cp_tx0 + x * tile_width; | ||||||
|  |             unsigned tx1 = tx0 + tile_width; | ||||||
|  |             unsigned pixx, pixw; | ||||||
|  |             unsigned data_size; | ||||||
|  | 
 | ||||||
|  |             if (tx0 < params.image_offset_x0) | ||||||
|  |                 tx0 = params.image_offset_x0; | ||||||
|  |             if (tx1 > xsiz) | ||||||
|  |                 tx1 = xsiz; | ||||||
|  | 
 | ||||||
|  |             pixx = tx0 - params.image_offset_x0; | ||||||
|  |             pixw = tx1 - tx0; | ||||||
|  | 
 | ||||||
|  |             pack(im, state->buffer, pixx, pixy, pixw, pixh); | ||||||
|  | 
 | ||||||
|  |             data_size = pixw * pixh * components; | ||||||
|  | 
 | ||||||
|  |             if (!opj_write_tile(codec, tile_ndx++, state->buffer, | ||||||
|  |                                 data_size, stream)) { | ||||||
|  |                 state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |                 state->state = J2K_STATE_FAILED; | ||||||
|  |                 goto quick_exit; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!opj_end_compress(codec, stream)) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     state->errcode = IMAGING_CODEC_END; | ||||||
|  |     state->state = J2K_STATE_DONE; | ||||||
|  |     ret = (int)ImagingIncrementalCodecBytesInBuffer(encoder); | ||||||
|  | 
 | ||||||
|  |  quick_exit: | ||||||
|  |     if (codec) | ||||||
|  |         opj_destroy_codec(codec); | ||||||
|  |     if (image) | ||||||
|  |         opj_image_destroy(image); | ||||||
|  |     if (stream) | ||||||
|  |         opj_stream_destroy(stream); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) | ||||||
|  | { | ||||||
|  |     JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; | ||||||
|  | 
 | ||||||
|  |     if (state->state == J2K_STATE_FAILED) | ||||||
|  |         return -1; | ||||||
|  | 
 | ||||||
|  |     if (state->state == J2K_STATE_START) { | ||||||
|  |         int seekable = (context->format != OPJ_CODEC_J2K  | ||||||
|  |                         ? INCREMENTAL_CODEC_SEEKABLE  | ||||||
|  |                         : INCREMENTAL_CODEC_NOT_SEEKABLE); | ||||||
|  | 
 | ||||||
|  |         context->encoder = ImagingIncrementalCodecCreate(j2k_encode_entry, | ||||||
|  |                                                          im, state, | ||||||
|  |                                                          INCREMENTAL_CODEC_WRITE, | ||||||
|  |                                                          seekable, | ||||||
|  |                                                          context->fd); | ||||||
|  | 
 | ||||||
|  |         if (!context->encoder) { | ||||||
|  |             state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |             state->state = J2K_STATE_FAILED; | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state->state = J2K_STATE_ENCODING; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ImagingIncrementalCodecPushBuffer(context->encoder, buf, bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Cleanup                                                              */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | ImagingJpeg2KEncodeCleanup(ImagingCodecState state) { | ||||||
|  |     JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; | ||||||
|  | 
 | ||||||
|  |     if (context->quality_layers) | ||||||
|  |         Py_DECREF(context->quality_layers); | ||||||
|  | 
 | ||||||
|  |     if (context->error_msg) | ||||||
|  |         free ((void *)context->error_msg); | ||||||
|  | 
 | ||||||
|  |     if (context->encoder) | ||||||
|  |         ImagingIncrementalCodecDestroy(context->encoder); | ||||||
|  | 
 | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif /* HAVE_OPENJPEG */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Local Variables: | ||||||
|  |  * c-basic-offset: 4 | ||||||
|  |  * End: | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | @ -192,6 +192,7 @@ if __name__ == "__main__": | ||||||
|     check_module("PIL CORE", "PIL._imaging") |     check_module("PIL CORE", "PIL._imaging") | ||||||
|     check_module("TKINTER", "PIL._imagingtk") |     check_module("TKINTER", "PIL._imagingtk") | ||||||
|     check_codec("JPEG", "jpeg") |     check_codec("JPEG", "jpeg") | ||||||
|  |     check_codec("JPEG 2000", "jpeg2k") | ||||||
|     check_codec("ZLIB (PNG/ZIP)", "zip") |     check_codec("ZLIB (PNG/ZIP)", "zip") | ||||||
|     check_codec("LIBTIFF", "libtiff") |     check_codec("LIBTIFF", "libtiff") | ||||||
|     check_module("FREETYPE2", "PIL._imagingft") |     check_module("FREETYPE2", "PIL._imagingft") | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								setup.py
									
									
									
									
									
								
							|  | @ -33,7 +33,8 @@ _LIB_IMAGING = ( | ||||||
|     "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point", |     "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point", | ||||||
|     "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", |     "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", | ||||||
|     "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", |     "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", | ||||||
|     "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode") |     "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental", | ||||||
|  |     "Jpeg2KDecode", "Jpeg2KEncode") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _add_directory(path, dir, where=None): | def _add_directory(path, dir, where=None): | ||||||
|  | @ -80,7 +81,8 @@ def _read(file): | ||||||
| 
 | 
 | ||||||
| try: | try: | ||||||
|     import _tkinter |     import _tkinter | ||||||
| except ImportError: | except (ImportError, OSError): | ||||||
|  |     # pypy emits an oserror | ||||||
|     _tkinter = None |     _tkinter = None | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -88,6 +90,7 @@ NAME = 'Pillow' | ||||||
| VERSION = '2.3.0' | VERSION = '2.3.0' | ||||||
| TCL_ROOT = None | TCL_ROOT = None | ||||||
| JPEG_ROOT = None | JPEG_ROOT = None | ||||||
|  | JPEG2K_ROOT = None | ||||||
| ZLIB_ROOT = None | ZLIB_ROOT = None | ||||||
| TIFF_ROOT = None | TIFF_ROOT = None | ||||||
| FREETYPE_ROOT = None | FREETYPE_ROOT = None | ||||||
|  | @ -98,6 +101,7 @@ class pil_build_ext(build_ext): | ||||||
| 
 | 
 | ||||||
|     class feature: |     class feature: | ||||||
|         zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None |         zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None | ||||||
|  |         jpeg2000 = None | ||||||
|         required = [] |         required = [] | ||||||
| 
 | 
 | ||||||
|         def require(self, feat): |         def require(self, feat): | ||||||
|  | @ -150,7 +154,7 @@ class pil_build_ext(build_ext): | ||||||
|         # |         # | ||||||
|         # add configured kits |         # add configured kits | ||||||
| 
 | 
 | ||||||
|         for root in (TCL_ROOT, JPEG_ROOT, TIFF_ROOT, ZLIB_ROOT, |         for root in (TCL_ROOT, JPEG_ROOT, JPEG2K_ROOT, TIFF_ROOT, ZLIB_ROOT, | ||||||
|                      FREETYPE_ROOT, LCMS_ROOT): |                      FREETYPE_ROOT, LCMS_ROOT): | ||||||
|             if isinstance(root, type(())): |             if isinstance(root, type(())): | ||||||
|                 lib_root, include_root = root |                 lib_root, include_root = root | ||||||
|  | @ -321,6 +325,16 @@ class pil_build_ext(build_ext): | ||||||
|         _add_directory(library_dirs, "/usr/lib") |         _add_directory(library_dirs, "/usr/lib") | ||||||
|         _add_directory(include_dirs, "/usr/include") |         _add_directory(include_dirs, "/usr/include") | ||||||
| 
 | 
 | ||||||
|  |         # on Windows, look for the OpenJPEG libraries in the location that | ||||||
|  |         # the official installed puts them | ||||||
|  |         if sys.platform == "win32": | ||||||
|  |             _add_directory(library_dirs,  | ||||||
|  |                            os.path.join(os.environ.get("ProgramFiles", ""), | ||||||
|  |                                         "OpenJPEG 2.0", "lib")) | ||||||
|  |             _add_directory(include_dirs, | ||||||
|  |                            os.path.join(os.environ.get("ProgramFiles", ""), | ||||||
|  |                                         "OpenJPEG 2.0", "include")) | ||||||
|  | 
 | ||||||
|         # |         # | ||||||
|         # insert new dirs *before* default libs, to avoid conflicts |         # insert new dirs *before* default libs, to avoid conflicts | ||||||
|         # between Python PYD stub libs and real libraries |         # between Python PYD stub libs and real libraries | ||||||
|  | @ -349,6 +363,11 @@ 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 | ||||||
| 
 | 
 | ||||||
|  |         if feature.want('jpeg2000'): | ||||||
|  |             if _find_include_file(self, "openjpeg-2.0/openjpeg.h"): | ||||||
|  |                 if _find_library_file(self, "openjp2"): | ||||||
|  |                     feature.jpeg2000 = "openjp2" | ||||||
|  |                      | ||||||
|         if feature.want('tiff'): |         if feature.want('tiff'): | ||||||
|             if _find_library_file(self, "tiff"): |             if _find_library_file(self, "tiff"): | ||||||
|                 feature.tiff = "tiff" |                 feature.tiff = "tiff" | ||||||
|  | @ -430,6 +449,11 @@ class pil_build_ext(build_ext): | ||||||
|         if feature.jpeg: |         if feature.jpeg: | ||||||
|             libs.append(feature.jpeg) |             libs.append(feature.jpeg) | ||||||
|             defs.append(("HAVE_LIBJPEG", None)) |             defs.append(("HAVE_LIBJPEG", None)) | ||||||
|  |         if feature.jpeg2000: | ||||||
|  |             libs.append(feature.jpeg2000) | ||||||
|  |             defs.append(("HAVE_OPENJPEG", None)) | ||||||
|  |             if sys.platform == "win32": | ||||||
|  |                 defs.append(("OPJ_STATIC", None)) | ||||||
|         if feature.zlib: |         if feature.zlib: | ||||||
|             libs.append(feature.zlib) |             libs.append(feature.zlib) | ||||||
|             defs.append(("HAVE_LIBZ", None)) |             defs.append(("HAVE_LIBZ", None)) | ||||||
|  | @ -537,6 +561,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.zlib, "ZLIB (PNG/ZIP)"), |             (feature.zlib, "ZLIB (PNG/ZIP)"), | ||||||
|             (feature.tiff, "LIBTIFF"), |             (feature.tiff, "LIBTIFF"), | ||||||
|             (feature.freetype, "FREETYPE2"), |             (feature.freetype, "FREETYPE2"), | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user