mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-30 23:47:27 +03:00 
			
		
		
		
	Added a JPEG 2000 decoder based on OpenJPEG.
This commit is contained in:
		
							parent
							
								
									1a03ca9224
								
							
						
					
					
						commit
						d6b8f0f666
					
				|  | @ -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 | ||||||
|  |  | ||||||
							
								
								
									
										189
									
								
								PIL/Jpeg2KImagePlugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								PIL/Jpeg2KImagePlugin.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,189 @@ | ||||||
|  | # | ||||||
|  | # 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" | ||||||
|  |             print 'Reading size/mode' | ||||||
|  |             try: | ||||||
|  |                 self.size, self.mode = _parse_codestream(self.fp) | ||||||
|  |             except Exception as e: | ||||||
|  |                 print e | ||||||
|  |             print '%r, %r' % (self.size, self.mode) | ||||||
|  |         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 | ||||||
|  | 
 | ||||||
|  |         self.tile = [('jpeg2k', (0, 0) + self.size, 0, | ||||||
|  |                       (self.codec, self.reduce, self.layers))] | ||||||
|  | 
 | ||||||
|  |     def load(self): | ||||||
|  |         if self.reduce: | ||||||
|  |             power = 1 << self.reduce | ||||||
|  |             self.size = (self.size[0] / power, self.size[1] / power) | ||||||
|  |          | ||||||
|  |         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') | ||||||
|  | 
 | ||||||
|  | # ------------------------------------------------------------ | ||||||
|  | # Registry stuff | ||||||
|  | 
 | ||||||
|  | Image.register_open("JPEG2000", Jpeg2KImageFile, _accept) | ||||||
|  | 
 | ||||||
|  | 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") | ||||||
|  | @ -33,6 +33,7 @@ _plugins = ['ArgImagePlugin', | ||||||
|             'ImtImagePlugin', |             'ImtImagePlugin', | ||||||
|             'IptcImagePlugin', |             'IptcImagePlugin', | ||||||
|             'JpegImagePlugin', |             'JpegImagePlugin', | ||||||
|  |             'Jpeg2KImagePlugin', | ||||||
|             'McIdasImagePlugin', |             'McIdasImagePlugin', | ||||||
|             'MicImagePlugin', |             'MicImagePlugin', | ||||||
|             'MpegImagePlugin', |             'MpegImagePlugin', | ||||||
|  |  | ||||||
|  | @ -3283,6 +3283,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); | ||||||
|  | @ -3351,6 +3352,9 @@ 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}, | ||||||
| #endif | #endif | ||||||
|     {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, |     {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, | ||||||
| #ifdef HAVE_LIBTIFF | #ifdef HAVE_LIBTIFF | ||||||
|  |  | ||||||
							
								
								
									
										76
									
								
								decode.c
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								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; | ||||||
|  | @ -194,6 +195,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 +208,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 +246,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 +776,63 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) | ||||||
|     return (PyObject*) decoder; |     return (PyObject*) decoder; | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* JPEG2000                                                             */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | #ifdef HAVE_OPENJPEG | ||||||
|  | 
 | ||||||
|  | /* We better define this decoder last in this file, so the following
 | ||||||
|  |    undef's won't mess things up for the Imaging library proper. */ | ||||||
|  | #undef  UINT8 | ||||||
|  | #undef  UINT16 | ||||||
|  | #undef  UINT32 | ||||||
|  | #undef  INT8 | ||||||
|  | #undef  INT16 | ||||||
|  | #undef  INT32 | ||||||
|  | 
 | ||||||
|  | #include "Jpeg2K.h" | ||||||
|  | 
 | ||||||
|  | PyObject* | ||||||
|  | PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) | ||||||
|  | { | ||||||
|  |     ImagingDecoderObject* decoder; | ||||||
|  |     JPEG2KSTATE *context; | ||||||
|  | 
 | ||||||
|  |     char* mode; | ||||||
|  |     char* format; | ||||||
|  |     OPJ_CODEC_FORMAT codec_format; | ||||||
|  |     int reduce = 0; | ||||||
|  |     int layers = 0; | ||||||
|  |     if (!PyArg_ParseTuple(args, "ss|ii", &mode, &format, | ||||||
|  |                           &reduce, &layers)) | ||||||
|  |         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(JPEG2KSTATE)); | ||||||
|  |     if (decoder == NULL) | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     decoder->handles_eof = 1; | ||||||
|  |     decoder->decode = ImagingJpeg2KDecode; | ||||||
|  |     decoder->cleanup = ImagingJpeg2KDecodeCleanup; | ||||||
|  | 
 | ||||||
|  |     context = (JPEG2KSTATE *)decoder->state.context; | ||||||
|  | 
 | ||||||
|  |     strncpy(context->mode, mode, 8); | ||||||
|  |     context->format = codec_format; | ||||||
|  |     context->reduce = reduce; | ||||||
|  |     context->layers = layers; | ||||||
|  |      | ||||||
|  |     return (PyObject*) decoder; | ||||||
|  | } | ||||||
|  | #endif /* HAVE_OPENJPEG */ | ||||||
|  |  | ||||||
|  | @ -424,6 +424,11 @@ 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); | ||||||
|  | #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 +502,19 @@ struct ImagingCodecStateInstance { | ||||||
|     void *context; |     void *context; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /* Incremental decoding support */ | ||||||
|  | typedef struct ImagingIncrementalDecoderStruct *ImagingIncrementalDecoder; | ||||||
|  | 
 | ||||||
|  | typedef int (*ImagingIncrementalDecoderEntry)(Imaging im,  | ||||||
|  |                                               ImagingCodecState state, | ||||||
|  |                                               ImagingIncrementalDecoder decoder); | ||||||
|  | 
 | ||||||
|  | extern ImagingIncrementalDecoder ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, Imaging im, ImagingCodecState state); | ||||||
|  | extern void ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder); | ||||||
|  | extern int ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, UINT8 *buf, int bytes); | ||||||
|  | size_t ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, void *buffer, size_t bytes); | ||||||
|  | off_t ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, off_t bytes); | ||||||
|  | 
 | ||||||
| /* Errcodes */ | /* Errcodes */ | ||||||
| #define	IMAGING_CODEC_END	 1 | #define	IMAGING_CODEC_END	 1 | ||||||
| #define	IMAGING_CODEC_OVERRUN	-1 | #define	IMAGING_CODEC_OVERRUN	-1 | ||||||
|  |  | ||||||
							
								
								
									
										366
									
								
								libImaging/Incremental.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								libImaging/Incremental.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,366 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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 | ||||||
|  |    ImagingIncrementalDecoderRead() call runs short on data, it suspends the | ||||||
|  |    decoding thread and wakes the main thread.  Conversely, the | ||||||
|  |    ImagingIncrementalDecodeData() 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. */ | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include <windows.h> | ||||||
|  | #include <process.h> | ||||||
|  | #else | ||||||
|  | #include <pthread.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | struct ImagingIncrementalStreamStruct { | ||||||
|  |   UINT8 *buffer; | ||||||
|  |   UINT8 *ptr; | ||||||
|  |   UINT8 *end; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct ImagingIncrementalDecoderStruct { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |   HANDLE            hDecodeEvent; | ||||||
|  |   HANDLE            hDataEvent; | ||||||
|  |   HANDLE            hThread; | ||||||
|  | #else | ||||||
|  |   pthread_mutex_t   start_mutex; | ||||||
|  |   pthread_cond_t    start_cond; | ||||||
|  |   pthread_mutex_t   decode_mutex; | ||||||
|  |   pthread_cond_t    decode_cond; | ||||||
|  |   pthread_mutex_t   data_mutex; | ||||||
|  |   pthread_cond_t    data_cond; | ||||||
|  |   pthread_t         thread; | ||||||
|  | #endif | ||||||
|  |   ImagingIncrementalDecoderEntry        entry; | ||||||
|  |   Imaging                               im; | ||||||
|  |   ImagingCodecState                     state; | ||||||
|  |   struct { | ||||||
|  |     UINT8 *buffer; | ||||||
|  |     UINT8 *ptr; | ||||||
|  |     UINT8 *end; | ||||||
|  |   } stream; | ||||||
|  |   int                                   started; | ||||||
|  |   int                                   result; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #if _WIN32 | ||||||
|  | static void __stdcall | ||||||
|  | incremental_thread(void *ptr) | ||||||
|  | { | ||||||
|  |   ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)ptr; | ||||||
|  | 
 | ||||||
|  |   decoder->result = decoder->entry(decoder->im, decoder->state, decoder); | ||||||
|  | 
 | ||||||
|  |   SetEvent(decoder->hDecodeEvent); | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | static void * | ||||||
|  | incremental_thread(void *ptr) | ||||||
|  | { | ||||||
|  |   ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)ptr; | ||||||
|  | 
 | ||||||
|  |   decoder->result = decoder->entry(decoder->im, decoder->state, decoder); | ||||||
|  | 
 | ||||||
|  |   pthread_cond_signal(&decoder->decode_cond); | ||||||
|  | 
 | ||||||
|  |   return NULL; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Create a new incremental decoder */ | ||||||
|  | ImagingIncrementalDecoder | ||||||
|  | ImagingIncrementalDecoderCreate(ImagingIncrementalDecoderEntry decoder_entry, | ||||||
|  |                                 Imaging im, | ||||||
|  |                                 ImagingCodecState state) | ||||||
|  | { | ||||||
|  |   ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)malloc(sizeof(struct ImagingIncrementalDecoderStruct)); | ||||||
|  | 
 | ||||||
|  |   decoder->entry = decoder_entry; | ||||||
|  |   decoder->im = im; | ||||||
|  |   decoder->state = state; | ||||||
|  |   decoder->result = 0; | ||||||
|  |   decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL; | ||||||
|  |   decoder->started = 0; | ||||||
|  | 
 | ||||||
|  |   /* System specific set-up */ | ||||||
|  | #if _WIN32 | ||||||
|  |   decoder->hDecodeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | ||||||
|  | 
 | ||||||
|  |   if (!decoder->hDecodeEvent) { | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   decoder->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | ||||||
|  | 
 | ||||||
|  |   if (!decoder->hDataEvent) { | ||||||
|  |     CloseHandle(decoder->hDecodeEvent); | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   decoder->hThread = _beginthreadex(NULL, 0, incremental_thread, decoder, | ||||||
|  |                                     CREATE_SUSPENDED, NULL); | ||||||
|  | 
 | ||||||
|  |   if (!decoder->hThread) { | ||||||
|  |     CloseHandle(decoder->hDecodeEvent); | ||||||
|  |     CloseHandle(decoder->hDataEvent); | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | #else | ||||||
|  |   if (pthread_mutex_init(&decoder->start_mutex, NULL)) { | ||||||
|  |     free (decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_mutex_init(&decoder->decode_mutex, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&decoder->start_mutex); | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_mutex_init(&decoder->data_mutex, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&decoder->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->decode_mutex); | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_cond_init(&decoder->start_cond, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&decoder->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->decode_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->data_mutex); | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_cond_init(&decoder->decode_cond, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&decoder->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->decode_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->data_mutex); | ||||||
|  |     pthread_cond_destroy(&decoder->start_cond); | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_cond_init(&decoder->data_cond, NULL)) { | ||||||
|  |     pthread_mutex_destroy(&decoder->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->decode_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->data_mutex); | ||||||
|  |     pthread_cond_destroy(&decoder->start_cond); | ||||||
|  |     pthread_cond_destroy(&decoder->decode_cond); | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (pthread_create(&decoder->thread, NULL, incremental_thread, decoder)) { | ||||||
|  |     pthread_mutex_destroy(&decoder->start_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->decode_mutex); | ||||||
|  |     pthread_mutex_destroy(&decoder->data_mutex); | ||||||
|  |     pthread_cond_destroy(&decoder->start_cond); | ||||||
|  |     pthread_cond_destroy(&decoder->decode_cond); | ||||||
|  |     pthread_cond_destroy(&decoder->data_cond); | ||||||
|  |     free(decoder); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   return decoder; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Destroy an incremental decoder */ | ||||||
|  | void | ||||||
|  | ImagingIncrementalDecoderDestroy(ImagingIncrementalDecoder decoder) | ||||||
|  | { | ||||||
|  |   if (!decoder->started) { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     ResumeThread(decoder->hThread); | ||||||
|  | #else | ||||||
|  |     pthread_cond_signal(&decoder->start_cond); | ||||||
|  | #endif | ||||||
|  |     decoder->started = 1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_lock(&decoder->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   decoder->stream.buffer = decoder->stream.ptr = decoder->stream.end = NULL; | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  |   SetEvent(decoder->hDataEvent); | ||||||
|  | 
 | ||||||
|  |   WaitForSingleObject(decoder->hThread, INFINITE); | ||||||
|  | 
 | ||||||
|  |   CloseHandle(decoder->hThread); | ||||||
|  |   CloseHandle(decoder->hDecodeEvent); | ||||||
|  |   CloseHandle(decoder->hDataEvent); | ||||||
|  | #else | ||||||
|  |   pthread_cond_signal(&decoder->data_cond); | ||||||
|  |   pthread_mutex_unlock(&decoder->data_mutex); | ||||||
|  | 
 | ||||||
|  |   pthread_join(decoder->thread, NULL); | ||||||
|  | 
 | ||||||
|  |   pthread_mutex_destroy(&decoder->start_mutex); | ||||||
|  |   pthread_mutex_destroy(&decoder->decode_mutex); | ||||||
|  |   pthread_mutex_destroy(&decoder->data_mutex); | ||||||
|  |   pthread_cond_destroy(&decoder->start_cond); | ||||||
|  |   pthread_cond_destroy(&decoder->decode_cond); | ||||||
|  |   pthread_cond_destroy(&decoder->data_cond); | ||||||
|  | #endif | ||||||
|  |   free (decoder); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Decode data using an incremental decoder */ | ||||||
|  | int | ||||||
|  | ImagingIncrementalDecodeData(ImagingIncrementalDecoder decoder, | ||||||
|  |                              UINT8 *buf, int bytes) | ||||||
|  | { | ||||||
|  |   if (!decoder->started) { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     ResumeThread(decoder->hThread); | ||||||
|  | #else | ||||||
|  |     pthread_cond_signal(&decoder->start_cond); | ||||||
|  | #endif | ||||||
|  |     decoder->started = 1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  |   pthread_mutex_lock(&decoder->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   decoder->stream.buffer = decoder->stream.ptr = buf; | ||||||
|  |   decoder->stream.end = buf + bytes; | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  |   SetEvent(decoder->hDataEvent); | ||||||
|  |   WaitForSingleObject(decoder->hDecodeEvent); | ||||||
|  | #else | ||||||
|  |   pthread_cond_signal(&decoder->data_cond); | ||||||
|  |   pthread_mutex_unlock(&decoder->data_mutex); | ||||||
|  | 
 | ||||||
|  |   pthread_mutex_lock(&decoder->decode_mutex); | ||||||
|  |   pthread_cond_wait(&decoder->decode_cond, &decoder->decode_mutex); | ||||||
|  |   pthread_mutex_unlock(&decoder->decode_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   return decoder->result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t | ||||||
|  | ImagingIncrementalDecoderRead(ImagingIncrementalDecoder decoder, | ||||||
|  |                              void *buffer, size_t bytes) | ||||||
|  | { | ||||||
|  |   UINT8 *ptr = (UINT8 *)buffer; | ||||||
|  |   size_t done = 0; | ||||||
|  | 
 | ||||||
|  |   pthread_mutex_lock(&decoder->data_mutex); | ||||||
|  |   while (bytes) { | ||||||
|  |     size_t todo = bytes; | ||||||
|  |     size_t remaining = decoder->stream.end - decoder->stream.ptr; | ||||||
|  | 
 | ||||||
|  |     if (!remaining) { | ||||||
|  | #ifndef _WIN32 | ||||||
|  |       pthread_mutex_lock(&decoder->decode_mutex); | ||||||
|  | #endif | ||||||
|  |       decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); | ||||||
|  | #if _WIN32 | ||||||
|  |       SetEvent(decoder->hDecodeEvent); | ||||||
|  |       WaitForSingleObject(decoder->hDataEvent); | ||||||
|  | #else | ||||||
|  |       pthread_cond_signal(&decoder->decode_cond); | ||||||
|  |       pthread_mutex_unlock(&decoder->decode_mutex); | ||||||
|  |       pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |       remaining = decoder->stream.end - decoder->stream.ptr; | ||||||
|  |     } | ||||||
|  |     if (todo > remaining) | ||||||
|  |       todo = remaining; | ||||||
|  | 
 | ||||||
|  |     if (!todo) | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     memcpy (ptr, decoder->stream.ptr, todo); | ||||||
|  |     decoder->stream.ptr += todo; | ||||||
|  |     bytes -= todo; | ||||||
|  |     done += todo; | ||||||
|  |     ptr += todo; | ||||||
|  |   } | ||||||
|  |   pthread_mutex_unlock(&decoder->data_mutex); | ||||||
|  | 
 | ||||||
|  |   return done; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | off_t | ||||||
|  | ImagingIncrementalDecoderSkip(ImagingIncrementalDecoder decoder, | ||||||
|  |                               off_t bytes) | ||||||
|  | { | ||||||
|  |   off_t done = 0; | ||||||
|  | 
 | ||||||
|  |   while (bytes) { | ||||||
|  |     off_t todo = bytes; | ||||||
|  |     off_t remaining = decoder->stream.end - decoder->stream.ptr; | ||||||
|  | 
 | ||||||
|  |     if (!remaining) { | ||||||
|  |       decoder->result = (int)(decoder->stream.ptr - decoder->stream.buffer); | ||||||
|  | #if _WIN32 | ||||||
|  |       SetEvent(decoder->hDecodeEvent); | ||||||
|  |       WaitForSingleObject(decoder->hDataEvent); | ||||||
|  | #else | ||||||
|  |       pthread_cond_signal(&decoder->decode_cond); | ||||||
|  |       pthread_mutex_lock(&decoder->data_mutex); | ||||||
|  |       pthread_cond_wait(&decoder->data_cond, &decoder->data_mutex); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |       remaining = decoder->stream.end - decoder->stream.ptr; | ||||||
|  |     } | ||||||
|  |     if (todo > remaining) | ||||||
|  |       todo = remaining; | ||||||
|  | 
 | ||||||
|  |     if (!todo) | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     decoder->stream.ptr += todo; | ||||||
|  |     bytes -= todo; | ||||||
|  |     done += todo; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return done; | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										44
									
								
								libImaging/Jpeg2K.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								libImaging/Jpeg2K.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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 */ | ||||||
|  | 
 | ||||||
|  |     /* Output mode */ | ||||||
|  |     char           mode[8]; | ||||||
|  | 
 | ||||||
|  |     /* 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; | ||||||
|  | 
 | ||||||
|  |     ImagingIncrementalDecoder decoder; | ||||||
|  | 
 | ||||||
|  |     opj_stream_t  *stream; | ||||||
|  | } JPEG2KSTATE; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Local Variables: | ||||||
|  |  * c-basic-offset: 4 | ||||||
|  |  * End: | ||||||
|  |  * | ||||||
|  |  */ | ||||||
							
								
								
									
										475
									
								
								libImaging/Jpeg2KDecode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								libImaging/Jpeg2KDecode.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,475 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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 <unistd.h> | ||||||
|  | #include "Jpeg2K.h" | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Error handler                                                        */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2k_error(const char *msg, void *client_data) | ||||||
|  | { | ||||||
|  |     JPEG2KSTATE *state = (JPEG2KSTATE *) 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) | ||||||
|  | { | ||||||
|  |     ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; | ||||||
|  | 
 | ||||||
|  |     return ImagingIncrementalDecoderRead(decoder, p_buffer, p_nb_bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static OPJ_SIZE_T | ||||||
|  | j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) | ||||||
|  | { | ||||||
|  |     return OPJ_FALSE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static OPJ_OFF_T | ||||||
|  | j2k_skip(OPJ_OFF_T p_nb_bytes, void *p_user_data) | ||||||
|  | { | ||||||
|  |     ImagingIncrementalDecoder decoder = (ImagingIncrementalDecoder)p_user_data; | ||||||
|  | 
 | ||||||
|  |     return ImagingIncrementalDecoderSkip(decoder, p_nb_bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static OPJ_BOOL | ||||||
|  | j2k_seek(OPJ_OFF_T p_nb_bytes, void *p_user_data) | ||||||
|  | { | ||||||
|  |     // We *deliberately* don't implement this
 | ||||||
|  |     return OPJ_FALSE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Unpackers                                                            */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | typedef void (*j2k_unpacker_t)(opj_image_t *in, 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, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; | ||||||
|  |     unsigned w = in->comps[0].w, h = in->comps[0].h; | ||||||
|  |     int shift = 8 - in->comps[0].prec; | ||||||
|  |     int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; | ||||||
|  |     unsigned x, y; | ||||||
|  | 
 | ||||||
|  |     if (shift < 0) | ||||||
|  |         offset += 1 << (-shift - 1); | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         OPJ_INT32 *data = &in->comps[0].data[y * w]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; | ||||||
|  |         for (x = 0; x < w; ++x) | ||||||
|  |             *row++ = j2ku_shift(offset + *data++, shift); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_gray_rgb(opj_image_t *in, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; | ||||||
|  |     unsigned w = in->comps[0].w, h = in->comps[0].h; | ||||||
|  |     int shift = 8 - in->comps[0].prec; | ||||||
|  |     int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; | ||||||
|  |     unsigned x, y; | ||||||
|  | 
 | ||||||
|  |     if (shift < 0) | ||||||
|  |         offset += 1 << (-shift - 1); | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         OPJ_INT32 *data = &in->comps[0].data[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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_graya_la(opj_image_t *in, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; | ||||||
|  |     unsigned w = in->comps[0].w, h = in->comps[0].h; | ||||||
|  |     int shift = 8 - in->comps[0].prec; | ||||||
|  |     int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; | ||||||
|  |     int ashift = 8 - in->comps[1].prec; | ||||||
|  |     int aoffset = in->comps[1].sgnd ? 1 << (in->comps[1].prec - 1) : 0; | ||||||
|  |     unsigned x, y; | ||||||
|  | 
 | ||||||
|  |     if (shift < 0) | ||||||
|  |         offset += 1 << (-shift - 1); | ||||||
|  |     if (ashift < 0) | ||||||
|  |         aoffset += 1 << (-ashift - 1); | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         OPJ_INT32 *data = &in->comps[0].data[y * w]; | ||||||
|  |         OPJ_INT32 *adata = &in->comps[1].data[y * w]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             UINT8 byte = j2ku_shift(offset + *data++, shift); | ||||||
|  |             row[0] = row[1] = row[2] = byte; | ||||||
|  |             row[3] = (unsigned)(offset + *adata++) >> shift; | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_srgb_rgb(opj_image_t *in, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; | ||||||
|  |     unsigned w = in->comps[0].w, h = in->comps[0].h; | ||||||
|  |     int shifts[3], offsets[3]; | ||||||
|  |     unsigned n, x, y; | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < 3; ++n) { | ||||||
|  |         shifts[n] = 8 - in->comps[n].prec; | ||||||
|  |         offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; | ||||||
|  |         if (shifts[n] < 0) | ||||||
|  |             offsets[n] += 1 << (-shifts[n] - 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         OPJ_INT32 *data[3]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         for (n = 0; n < 3; ++n) | ||||||
|  |             data[n] = &in->comps[n].data[y * w]; | ||||||
|  |          | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             for (n = 0; n < 3; ++n) | ||||||
|  |                 row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); | ||||||
|  |             row[3] = 0xff; | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_sycc_rgb(opj_image_t *in, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; | ||||||
|  |     unsigned w = in->comps[0].w, h = in->comps[0].h; | ||||||
|  |     int shifts[3], offsets[3]; | ||||||
|  |     unsigned n, x, y; | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < 3; ++n) { | ||||||
|  |         shifts[n] = 8 - in->comps[n].prec; | ||||||
|  |         offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; | ||||||
|  |         if (shifts[n] < 0) | ||||||
|  |             offsets[n] += 1 << (-shifts[n] - 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         OPJ_INT32 *data[3]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         UINT8 *row_start = row; | ||||||
|  |         for (n = 0; n < 3; ++n) | ||||||
|  |             data[n] = &in->comps[n].data[y * w]; | ||||||
|  |          | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             for (n = 0; n < 3; ++n) | ||||||
|  |                 row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); | ||||||
|  |             row[3] = 0xff; | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  |         ImagingConvertYCbCr2RGB(row_start, row_start, w); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | j2ku_srgba_rgba(opj_image_t *in, Imaging im) | ||||||
|  | { | ||||||
|  |     unsigned x0 = in->comps[0].x0, y0 = in->comps[0].y0; | ||||||
|  |     unsigned w = in->comps[0].w, h = in->comps[0].h; | ||||||
|  |     int shifts[4], offsets[4]; | ||||||
|  |     unsigned n, x, y; | ||||||
|  | 
 | ||||||
|  |     for (n = 0; n < 4; ++n) { | ||||||
|  |         shifts[n] = 8 - in->comps[n].prec; | ||||||
|  |         offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; | ||||||
|  |         if (shifts[n] < 0) | ||||||
|  |             offsets[n] += 1 << (-shifts[n] - 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (y = 0; y < h; ++y) { | ||||||
|  |         OPJ_INT32 *data[4]; | ||||||
|  |         UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; | ||||||
|  |         for (n = 0; n < 4; ++n) | ||||||
|  |             data[n] = &in->comps[n].data[y * w]; | ||||||
|  |          | ||||||
|  |         for (x = 0; x < w; ++x) { | ||||||
|  |             for (n = 0; n < 4; ++n) | ||||||
|  |                 row[n] = j2ku_shift(offsets[n] + *data[n]++, shifts[n]); | ||||||
|  |             row += 4; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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 }, | ||||||
|  |     { "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 }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* 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, | ||||||
|  |                  ImagingIncrementalDecoder decoder) | ||||||
|  | { | ||||||
|  |     JPEG2KSTATE *context = (JPEG2KSTATE *) state->context; | ||||||
|  |     opj_stream_t *stream; | ||||||
|  |     opj_image_t *image; | ||||||
|  |     opj_codec_t *codec; | ||||||
|  |     opj_dparameters_t params; | ||||||
|  |     OPJ_COLOR_SPACE color_space; | ||||||
|  |     j2k_unpacker_t unpack; | ||||||
|  |     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_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, context->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) { | ||||||
|  |         /* Check that the sample frequency is uniform */ | ||||||
|  |         if (image->comps[0].dx != image->comps[n].dx | ||||||
|  |             || image->comps[0].dy != image->comps[n].dy) { | ||||||
|  |             state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |             state->state = J2K_STATE_FAILED; | ||||||
|  |             goto quick_exit; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /* Check that the bit depth is uniform */ | ||||||
|  |         if (image->comps[0].prec != image->comps[n].prec) { | ||||||
|  |             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 (context->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 and unpack the image */ | ||||||
|  |     if (!opj_decode(codec, stream, image) | ||||||
|  |         || !opj_end_decompress(codec, stream)) { | ||||||
|  |         state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |         state->state = J2K_STATE_FAILED; | ||||||
|  |         goto quick_exit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unpack(image, im); | ||||||
|  | 
 | ||||||
|  |  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) | ||||||
|  | { | ||||||
|  |     JPEG2KSTATE *context = (JPEG2KSTATE *) state->context; | ||||||
|  | 
 | ||||||
|  |     if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED) | ||||||
|  |         return -1; | ||||||
|  | 
 | ||||||
|  |     if (state->state == J2K_STATE_START) { | ||||||
|  |         context->decoder = ImagingIncrementalDecoderCreate(j2k_decode_entry, | ||||||
|  |                                                            im, state); | ||||||
|  | 
 | ||||||
|  |         if (!context->decoder) { | ||||||
|  |             state->errcode = IMAGING_CODEC_BROKEN; | ||||||
|  |             state->state = J2K_STATE_FAILED; | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state->state = J2K_STATE_DECODING; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ImagingIncrementalDecodeData(context->decoder, buf, bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | /* Cleanup                                                              */ | ||||||
|  | /* -------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | int | ||||||
|  | ImagingJpeg2KDecodeCleanup(ImagingCodecState state) { | ||||||
|  |     JPEG2KSTATE *context = (JPEG2KSTATE *)state->context; | ||||||
|  | 
 | ||||||
|  |     if (context->decoder) | ||||||
|  |         ImagingIncrementalDecoderDestroy(context->decoder); | ||||||
|  | 
 | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char * | ||||||
|  | ImagingJpeg2KVersion(void) | ||||||
|  | { | ||||||
|  |     return opj_version(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif /* HAVE_OPENJPEG */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Local Variables: | ||||||
|  |  * c-basic-offset: 4 | ||||||
|  |  * End: | ||||||
|  |  * | ||||||
|  |  */ | ||||||
							
								
								
									
										13
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								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") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _add_directory(path, dir, where=None): | def _add_directory(path, dir, where=None): | ||||||
|  | @ -98,6 +99,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): | ||||||
|  | @ -349,6 +351,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 +437,9 @@ 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 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 +547,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