diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 6217b2f92..b21e9e317 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -185,53 +185,59 @@ class ImageFile(Image.Image): except AttributeError: prefix = b"" - for d, e, o, a in self.tile: - d = Image._getdecoder(self.mode, d, a, self.decoderconfig) - seek(o) + for decoder_name, extents, offset, args in self.tile: + decoder = Image._getdecoder(self.mode, decoder_name, + args, self.decoderconfig) + seek(offset) try: - d.setimage(self.im, e) + decoder.setimage(self.im, extents) except ValueError: continue - b = prefix - while True: - try: - s = read(self.decodermaxblock) - except (IndexError, struct.error): # truncated png/gif - if LOAD_TRUNCATED_IMAGES: + if decoder.pulls_fd: + decoder.setfd(self.fp) + status, err_code = decoder.decode(b"") + else: + b = prefix + while True: + try: + s = read(self.decodermaxblock) + except (IndexError, struct.error): # truncated png/gif + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IOError("image file is truncated") + + if not s and not decoder.handles_eof: # truncated jpeg + self.tile = [] + + # JpegDecode needs to clean things up here either way + # If we don't destroy the decompressor, + # we have a memory leak. + decoder.cleanup() + + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IOError("image file is truncated " + "(%d bytes not processed)" % len(b)) + + b = b + s + n, err_code = decoder.decode(b) + if n < 0: break - else: - raise IOError("image file is truncated") - - if not s and not d.handles_eof: # truncated jpeg - self.tile = [] - - # JpegDecode needs to clean things up here either way - # If we don't destroy the decompressor, - # we have a memory leak. - d.cleanup() - - if LOAD_TRUNCATED_IMAGES: - break - else: - raise IOError("image file is truncated " - "(%d bytes not processed)" % len(b)) - - b = b + s - n, e = d.decode(b) - if n < 0: - break - b = b[n:] + b = b[n:] + # Need to cleanup here to prevent leaks in PyPy - d.cleanup() + decoder.cleanup() self.tile = [] self.readonly = readonly self.fp = None # might be shared - if not self.map and not LOAD_TRUNCATED_IMAGES and e < 0: + if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: # still raised if decoder fails to return anything - raise_ioerror(e) + raise_ioerror(err_code) # post processing if hasattr(self, "tile_post_rotate"): @@ -465,11 +471,15 @@ def _save(im, fp, tile, bufsize=0): if o > 0: fp.seek(o, 0) e.setimage(im.im, b) - while True: - l, s, d = e.encode(bufsize) - fp.write(d) - if s: - break + if e.pushes_fd: + e.setfd(fp) + l,s = e.encode_to_pyfd() + else: + while True: + l, s, d = e.encode(bufsize) + fp.write(d) + if s: + break if s < 0: raise IOError("encoder error %d when writing image file" % s) e.cleanup() @@ -480,7 +490,11 @@ def _save(im, fp, tile, bufsize=0): if o > 0: fp.seek(o, 0) e.setimage(im.im, b) - s = e.encode_to_file(fh, bufsize) + if e.pushes_fd: + e.setfd(fp) + l,s = e.encode_to_pyfd() + else: + s = e.encode_to_file(fh, bufsize) if s < 0: raise IOError("encoder error %d when writing image file" % s) e.cleanup() diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index 238500448..02b1e53f5 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -192,7 +192,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile): length = -1 self.tile = [('jpeg2k', (0, 0) + self.size, 0, - (self.codec, self.reduce, self.layers, fd, length))] + (self.codec, self.reduce, self.layers, fd, length, self.fp))] def load(self): if self.reduce: diff --git a/decode.c b/decode.c index 8313c25ad..7bb88d94e 100644 --- a/decode.c +++ b/decode.c @@ -52,7 +52,8 @@ typedef struct { struct ImagingCodecStateInstance state; Imaging im; PyObject* lock; - int handles_eof; + int handles_eof; + int pulls_fd; } ImagingDecoderObject; static PyTypeObject ImagingDecoderType; @@ -97,6 +98,10 @@ PyImaging_DecoderNew(int contextsize) /* Most decoders don't want to handle EOF themselves */ decoder->handles_eof = 0; + /* set if the decoder needs to pull data from the fd, instead of + having it pushed */ + decoder->pulls_fd = 0; + return decoder; } @@ -108,6 +113,7 @@ _dealloc(ImagingDecoderObject* decoder) free(decoder->state.buffer); free(decoder->state.context); Py_XDECREF(decoder->lock); + Py_XDECREF(decoder->state.fd); PyObject_Del(decoder); } @@ -121,11 +127,15 @@ _decode(ImagingDecoderObject* decoder, PyObject* args) if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize)) return NULL; - ImagingSectionEnter(&cookie); + if (!decoder->pulls_fd) { + ImagingSectionEnter(&cookie); + } status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize); - ImagingSectionLeave(&cookie); + if (!decoder->pulls_fd) { + ImagingSectionLeave(&cookie); + } return Py_BuildValue("ii", status, decoder->state.errcode); } @@ -210,16 +220,42 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) return Py_None; } +static PyObject* +_setfd(ImagingDecoderObject* decoder, PyObject* args) +{ + PyObject* fd; + ImagingCodecState state; + + if (!PyArg_ParseTuple(args, "O", &fd)) + return NULL; + + state = &decoder->state; + + Py_XINCREF(fd); + state->fd = fd; + + Py_INCREF(Py_None); + return Py_None; +} + + static PyObject * _get_handles_eof(ImagingDecoderObject *decoder) { return PyBool_FromLong(decoder->handles_eof); } +static PyObject * +_get_pulls_fd(ImagingDecoderObject *decoder) +{ + return PyBool_FromLong(decoder->pulls_fd); +} + static struct PyMethodDef methods[] = { {"decode", (PyCFunction)_decode, 1}, {"cleanup", (PyCFunction)_decode_cleanup, 1}, {"setimage", (PyCFunction)_setimage, 1}, + {"setfd", (PyCFunction)_setfd, 1}, {NULL, NULL} /* sentinel */ }; @@ -227,6 +263,9 @@ static struct PyGetSetDef getseters[] = { {"handles_eof", (getter)_get_handles_eof, NULL, "True if this decoder expects to handle EOF itself.", NULL}, + {"pulls_fd", (getter)_get_pulls_fd, NULL, + "True if this decoder expects to pull from self.fd itself.", + NULL}, {NULL, NULL, NULL, NULL, NULL} /* sentinel */ }; @@ -811,6 +850,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) int layers = 0; int fd = -1; PY_LONG_LONG length = -1; + if (!PyArg_ParseTuple(args, "ss|iiiL", &mode, &format, &reduce, &layers, &fd, &length)) return NULL; @@ -829,11 +869,12 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) return NULL; decoder->handles_eof = 1; + decoder->pulls_fd = 1; decoder->decode = ImagingJpeg2KDecode; decoder->cleanup = ImagingJpeg2KDecodeCleanup; context = (JPEG2KDECODESTATE *)decoder->state.context; - + context->fd = fd; context->length = (off_t)length; context->format = codec_format; @@ -843,3 +884,4 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) return (PyObject*) decoder; } #endif /* HAVE_OPENJPEG */ + diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index 61fcba6e6..6c4bc6d6a 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -1,15 +1,27 @@ -.. _file-decoders: +.. _image-plugins: -Writing Your Own File Decoder +Writing Your Own Image Plugin ============================= -The Python Imaging Library uses a plug-in model which allows you to -add your own decoders to the library, without any changes to the -library itself. Such plug-ins usually have names like +The Pillow uses a plug-in model which allows you to add your own +decoders to the library, without any changes to the library +itself. Such plug-ins usually have names like :file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name (usually an abbreviation). -.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually. +.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your image plugin manually. + +Pillow decodes files in 2 stages: + +1. It loops over the available image plugins in the loaded order, and + calls the plugin's ``accept` function with the first 16 bytes of + the file. If the ``accept`` function returns true, the plugin's + ``_open`` method is called to set up the image metadata and image + tiles. The ``_open`` method is not for decoding the actual image + data. +2. When the image data is requested, the ``ImageFile.load`` method is + called, which sets up a decoder for each tile and feeds the data to + it. A decoder plug-in should contain a decoder class, based on the :py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an @@ -22,6 +34,11 @@ call to the :py:mod:`~PIL.Image` module. For performance reasons, it is important that the :py:meth:`_open` method quickly rejects files that do not have the appropriate contents. +The ``raw`` decoder is useful for uncompressed image formats, but many +formats require more control of the decoding context, either with a +decoder written in ``C`` or by linking in an external library to do +the decoding. (Examples of this include PNG, Tiff, and Jpeg support) + Example ------- @@ -74,11 +91,13 @@ true color. Image.register_extension(SpamImageFile.format, ".spam") Image.register_extension(SpamImageFile.format, ".spa") # dos version -The format handler must always set the :py:attr:`~PIL.Image.Image.size` and -:py:attr:`~PIL.Image.Image.mode` attributes. If these are not set, the file -cannot be opened. To simplify the decoder, the calling code considers -exceptions like :py:exc:`SyntaxError`, :py:exc:`KeyError`, and -:py:exc:`IndexError`, as a failure to identify the file. +The format handler must always set the +:py:attr:`~PIL.Image.Image.size` and :py:attr:`~PIL.Image.Image.mode` +attributes. If these are not set, the file cannot be opened. To +simplify the decoder, the calling code considers exceptions like +:py:exc:`SyntaxError`, :py:exc:`KeyError`, :py:exc:`IndexError`, +:py:exc:`EOFError` and :py:exc:`struct.error` as a failure to identify +the file. Note that the decoder must be explicitly registered using :py:func:`PIL.Image.register_open`. Although not required, it is also a good @@ -282,3 +301,93 @@ The fields are used as follows: **orientation** Whether the first line in the image is the top line on the screen (1), or the bottom line (-1). If omitted, the orientation defaults to 1. + +.. _file-decoders: + +Writing Your Own File Decoder +============================= + +There are 3 stages in a file decoder's lifetime: + +1. Setup: Pillow looks for a function named ``[decodername]_decoder`` + on the internal core image object. That function is called with the ``args`` tuple + from the ``tile`` setup in the ``_open`` method. + +2. Decoding: The decoder's decode function is repeadedly called with + chunks of image data. + +3. Cleanup: If the decoder has registered a cleanup function, it will + be called at the end of the decoding process, even if there was an + exception raised. + + +Setup +----- + +The current conventions are that the decoder setup function is named +``PyImaging_[Decodername]DecoderNew`` and defined in ``decode.c``. The +python binding for it is named ``[decodername]_decoder`` and is setup +from within the ``_imaging.c`` file in the codecs section of the +function array. + +The setup function needs to call ``PyImaging_DecoderNew`` and at the +very least, set the ``decode`` function pointer. The fields of +interest in this object are: + +**decode** + Function pointer to the decode function, which has access to + ``im``, ``state``, and the buffer of data to be added to the image. + +**cleanup** + Function pointer to the cleanup function, has access to ``state``. + +**im** + The target image, will be set by Pillow. + +**state** + An ImagingCodecStateInstance, will be set by Pillow. The **context** + member is an opaque struct that can be used by the decoder to store + any format spefific state or options. + +**handles_eof** + UNDONE, set if your code handles EOF errors. + +**pulls_fd** + **EXPERIMENTAL** -- **WARNING**, interface may change. If set to 1, + ``state->fd`` will be a pointer to the Python file like object. The + decoder may use the functions in ``codec_fd.c`` to read directly + from the file like object rather than have the data pushed through a + buffer. Note that this implementation may be refactored until this + warning is removed. + + .. versionadded:: 3.3.0 + + +Decoding +-------- + +The decode function is called with the target (core) image, the +decoder state structure, and a buffer of data to be decoded. + +**Experimental** -- If ``pulls_fd`` is set, then the decode function +is called once, with an empty buffer. It is the decoder's +responibility to decode the entire tile in that one call. The rest of +this secton only applies if ``pulls_fd`` is not set. + +It is the decoder's responsibility to pull as much data as possible +out of the buffer and return the number of bytes consumed. The next +call to the decoder will include the previous unconsumed tail. The +decoder function will be called multiple times as the data is read +from the file like object. + +If an error occurs, set ``state->errcode`` and return -1. + +Return -1 on success, without setting the errcode. + +Cleanup +------- + +The cleanup function is called after the decoder returns a negative +value, or if there is a read error from the file. This function should +free any allocated memory and release any resources from external +libraries. diff --git a/encode.c b/encode.c index b9f553061..b08a9aa57 100644 --- a/encode.c +++ b/encode.c @@ -44,6 +44,7 @@ typedef struct { struct ImagingCodecStateInstance state; Imaging im; PyObject* lock; + int pushes_fd; } ImagingEncoderObject; static PyTypeObject ImagingEncoderType; @@ -84,6 +85,7 @@ PyImaging_EncoderNew(int contextsize) /* Target image */ encoder->lock = NULL; encoder->im = NULL; + encoder->pushes_fd = 0; return encoder; } @@ -96,6 +98,7 @@ _dealloc(ImagingEncoderObject* encoder) free(encoder->state.buffer); free(encoder->state.context); Py_XDECREF(encoder->lock); + Py_XDECREF(encoder->state.fd); PyObject_Del(encoder); } @@ -143,6 +146,27 @@ _encode(ImagingEncoderObject* encoder, PyObject* args) return result; } +static PyObject* +_encode_to_pyfd(ImagingEncoderObject* encoder, PyObject* args) +{ + + PyObject *result; + int status; + + if (!encoder->pushes_fd) { + // UNDONE, appropriate errcode??? + result = Py_BuildValue("ii", 0, IMAGING_CODEC_CONFIG);; + return result; + } + + status = encoder->encode(encoder->im, &encoder->state, + (UINT8*) NULL, 0); + + result = Py_BuildValue("ii", status, encoder->state.errcode); + + return result; +} + static PyObject* _encode_to_file(ImagingEncoderObject* encoder, PyObject* args) { @@ -254,14 +278,47 @@ _setimage(ImagingEncoderObject* encoder, PyObject* args) return Py_None; } +static PyObject* +_setfd(ImagingEncoderObject* encoder, PyObject* args) +{ + PyObject* fd; + ImagingCodecState state; + + if (!PyArg_ParseTuple(args, "O", &fd)) + return NULL; + + state = &encoder->state; + + Py_XINCREF(fd); + state->fd = fd; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_get_pushes_fd(ImagingEncoderObject *encoder) +{ + return PyBool_FromLong(encoder->pushes_fd); +} + static struct PyMethodDef methods[] = { {"encode", (PyCFunction)_encode, 1}, {"cleanup", (PyCFunction)_encode_cleanup, 1}, {"encode_to_file", (PyCFunction)_encode_to_file, 1}, + {"encode_to_pyfd", (PyCFunction)_encode_to_pyfd, 1}, {"setimage", (PyCFunction)_setimage, 1}, + {"setfd", (PyCFunction)_setfd, 1}, {NULL, NULL} /* sentinel */ }; +static struct PyGetSetDef getseters[] = { + {"pushes_fd", (getter)_get_pushes_fd, NULL, + "True if this decoder expects to push directly to self.fd", + NULL}, + {NULL, NULL, NULL, NULL, NULL} /* sentinel */ +}; + static PyTypeObject ImagingEncoderType = { PyVarObject_HEAD_INIT(NULL, 0) "ImagingEncoder", /*tp_name*/ @@ -293,7 +350,7 @@ static PyTypeObject ImagingEncoderType = { 0, /*tp_iternext*/ methods, /*tp_methods*/ 0, /*tp_members*/ - 0, /*tp_getset*/ + getseters, /*tp_getset*/ }; /* -------------------------------------------------------------------- */ @@ -916,6 +973,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) encoder->encode = ImagingJpeg2KEncode; encoder->cleanup = ImagingJpeg2KEncodeCleanup; + encoder->pushes_fd = 1; context = (JPEG2KENCODESTATE *)encoder->state.context; @@ -923,6 +981,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) 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, diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index d0c59c9d0..03ea30751 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -486,33 +486,18 @@ struct ImagingCodecStateInstance { int bits, bytes; UINT8 *buffer; void *context; + PyObject *fd; }; -/* Incremental encoding/decoding support */ -typedef struct ImagingIncrementalCodecStruct *ImagingIncrementalCodec; -typedef int (*ImagingIncrementalCodecEntry)(Imaging im, - ImagingCodecState state, - ImagingIncrementalCodec codec); +/* Codec read/write python fd */ -enum { - INCREMENTAL_CODEC_READ = 1, - INCREMENTAL_CODEC_WRITE = 2 -}; +extern Py_ssize_t _imaging_read_pyFd(PyObject *fd, char* dest, Py_ssize_t bytes); +extern Py_ssize_t _imaging_write_pyFd(PyObject *fd, char* src, Py_ssize_t bytes); +extern int _imaging_seek_pyFd(PyObject *fd, Py_ssize_t offset, int whence); +extern Py_ssize_t _imaging_tell_pyFd(PyObject *fd); -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 Py_ssize_t ImagingIncrementalCodecRead(ImagingIncrementalCodec codec, void *buffer, size_t bytes); -extern off_t ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec, off_t bytes); -extern Py_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 */ #define IMAGING_CODEC_END 1 diff --git a/libImaging/Incremental.c b/libImaging/Incremental.c deleted file mode 100644 index f0e55244c..000000000 --- a/libImaging/Incremental.c +++ /dev/null @@ -1,687 +0,0 @@ -/* - * 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 -#else -#include -#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) -{ - /* malloc check ok, small constant allocation */ - 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; - - if (old_size > SIZE_MAX - bytes) { - codec->state->errcode = IMAGING_CODEC_MEMORY; -#ifndef _WIN32 - pthread_mutex_unlock(&codec->data_mutex); -#endif - return -1; - } - /* malloc check ok, overflow checked */ - 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; -} - -Py_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) { - Py_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); - Py_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; -} - -Py_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); -} diff --git a/libImaging/Jpeg2K.h b/libImaging/Jpeg2K.h index fd53b59df..b864d5e96 100644 --- a/libImaging/Jpeg2K.h +++ b/libImaging/Jpeg2K.h @@ -10,6 +10,9 @@ #include +/* 1MB for now */ +#define BUFFER_SIZE OPJ_J2K_STREAM_CHUNK_SIZE + /* -------------------------------------------------------------------- */ /* Decoder */ /* -------------------------------------------------------------------- */ @@ -20,6 +23,9 @@ typedef struct { /* File descriptor, if available; otherwise, -1 */ int fd; + /* File pointer, when opened */ + FILE * pfile; + /* Length of data, if available; otherwise, -1 */ off_t length; @@ -34,8 +40,7 @@ typedef struct { /* PRIVATE CONTEXT (set by decoder) */ const char *error_msg; - - ImagingIncrementalCodec decoder; + } JPEG2KDECODESTATE; /* -------------------------------------------------------------------- */ @@ -48,6 +53,9 @@ typedef struct { /* File descriptor, if available; otherwise, -1 */ int fd; + /* File pointer, when opened */ + FILE * pfile; + /* Specify the desired format */ OPJ_CODEC_FORMAT format; @@ -83,7 +91,7 @@ typedef struct { /* PRIVATE CONTEXT (set by decoder) */ const char *error_msg; - ImagingIncrementalCodec encoder; + } JPEG2KENCODESTATE; /* diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 239461c7c..b0cefe278 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -46,9 +46,9 @@ j2k_error(const char *msg, void *client_data) 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; + ImagingCodecState state = (ImagingCodecState)p_user_data; - size_t len = ImagingIncrementalCodecRead(decoder, p_buffer, p_nb_bytes); + size_t len = _imaging_read_pyFd(state->fd, p_buffer, p_nb_bytes); return len ? len : (OPJ_SIZE_T)-1; } @@ -56,8 +56,10 @@ j2k_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) 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); + ImagingCodecState state = (ImagingCodecState)p_user_data; + + _imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_CUR); + off_t pos = _imaging_tell_pyFd(state->fd); return pos ? pos : (OPJ_OFF_T)-1; } @@ -545,8 +547,7 @@ enum { }; static int -j2k_decode_entry(Imaging im, ImagingCodecState state, - ImagingIncrementalCodec decoder) +j2k_decode_entry(Imaging im, ImagingCodecState state) { JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context; opj_stream_t *stream = NULL; @@ -558,22 +559,22 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, size_t buffer_size = 0; unsigned n; - stream = opj_stream_default_create(OPJ_TRUE); - + stream = opj_stream_create(BUFFER_SIZE, 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); /* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */ #ifndef OPJ_VERSION_MAJOR - opj_stream_set_user_data(stream, decoder); + opj_stream_set_user_data(stream, state); #else - opj_stream_set_user_data(stream, decoder, NULL); + opj_stream_set_user_data(stream, state, NULL); /* Hack: if we don't know the length, the largest file we can possibly support is 4GB. We can't go larger than this, because @@ -749,6 +750,12 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, state->state = J2K_STATE_DONE; state->errcode = IMAGING_CODEC_END; + if (context->pfile) { + if(fclose(context->pfile)){ + context->pfile = NULL; + } + } + quick_exit: if (codec) opj_destroy_codec(codec); @@ -763,28 +770,28 @@ j2k_decode_entry(Imaging im, ImagingCodecState state, int ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { - JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *) state->context; + + if (bytes){ + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + return -1; + } 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 j2k_decode_entry(im, state); } - return ImagingIncrementalCodecPushBuffer(context->decoder, buf, bytes); + if (state->state == J2K_STATE_DECODING) { + state->errcode = IMAGING_CODEC_BROKEN; + state->state = J2K_STATE_FAILED; + return -1; + } + return -1; } /* -------------------------------------------------------------------- */ @@ -795,17 +802,12 @@ int ImagingJpeg2KDecodeCleanup(ImagingCodecState state) { JPEG2KDECODESTATE *context = (JPEG2KDECODESTATE *)state->context; - if (context->error_msg) + if (context->error_msg) { free ((void *)context->error_msg); - - if (context->decoder) - ImagingIncrementalCodecDestroy(context->decoder); - + } + context->error_msg = NULL; - /* Prevent multiple calls to ImagingIncrementalCodecDestroy */ - context->decoder = NULL; - return -1; } diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 16f44f539..1747ec1f1 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -36,6 +36,12 @@ j2k_error(const char *msg, void *client_data) state->error_msg = strdup(msg); } +static void +j2k_warn(const char *msg, void *client_data) +{ + // Null handler +} + /* -------------------------------------------------------------------- */ /* Buffer output stream */ /* -------------------------------------------------------------------- */ @@ -43,26 +49,43 @@ j2k_error(const char *msg, void *client_data) 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); + ImagingCodecState state = (ImagingCodecState)p_user_data; + int result; - return len ? len : (OPJ_SIZE_T)-1; + result = _imaging_write_pyFd(state->fd, p_buffer, p_nb_bytes); + + return result ? result : (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); + ImagingCodecState state = (ImagingCodecState)p_user_data; + char *buffer; + int result; - return pos ? pos : (OPJ_OFF_T)-1; + /* Explicitly write zeros */ + buffer = calloc(p_nb_bytes,1); + if (!buffer) { + return (OPJ_OFF_T)-1; + } + + result = _imaging_write_pyFd(state->fd, buffer, p_nb_bytes); + + free(buffer); + + return result ? result : p_nb_bytes; } 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); + ImagingCodecState state = (ImagingCodecState)p_user_data; + off_t pos = 0; + + _imaging_seek_pyFd(state->fd, p_nb_bytes, SEEK_SET); + pos = _imaging_tell_pyFd(state->fd); return pos == p_nb_bytes; } @@ -244,8 +267,7 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) } static int -j2k_encode_entry(Imaging im, ImagingCodecState state, - ImagingIncrementalCodec encoder) +j2k_encode_entry(Imaging im, ImagingCodecState state) { JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; opj_stream_t *stream = NULL; @@ -266,11 +288,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, unsigned prec = 8; unsigned bpp = 8; unsigned _overflow_scale_factor; - /* SIZE_MAX is not working in the conditionals unless it's a typed - variable */ - unsigned _SIZE__MAX = SIZE_MAX; - stream = opj_stream_default_create(OPJ_FALSE); + stream = opj_stream_create(BUFFER_SIZE, OPJ_FALSE); if (!stream) { state->errcode = IMAGING_CODEC_BROKEN; @@ -284,9 +303,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, /* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */ #ifndef OPJ_VERSION_MAJOR - opj_stream_set_user_data(stream, encoder); + opj_stream_set_user_data(stream, state); #else - opj_stream_set_user_data(stream, encoder, NULL); + opj_stream_set_user_data(stream, state, NULL); #endif /* Setup an opj_image */ @@ -465,6 +484,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, } opj_set_error_handler(codec, j2k_error, context); + opj_set_info_handler(codec, j2k_warn, context); + opj_set_warning_handler(codec, j2k_warn, context); opj_setup_encoder(codec, ¶ms, image); /* Start encoding */ @@ -483,10 +504,10 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, /* check for integer overflow for the malloc line, checking any expression that may multiply either tile_width or tile_height */ _overflow_scale_factor = components * prec; - if (( tile_width > _SIZE__MAX / _overflow_scale_factor ) || - ( tile_height > _SIZE__MAX / _overflow_scale_factor ) || - ( tile_width > _SIZE__MAX / (tile_height * _overflow_scale_factor )) || - ( tile_height > _SIZE__MAX / (tile_width * _overflow_scale_factor ))) { + if (( tile_width > UINT_MAX / _overflow_scale_factor ) || + ( tile_height > UINT_MAX / _overflow_scale_factor ) || + ( tile_width > UINT_MAX / (tile_height * _overflow_scale_factor )) || + ( tile_height > UINT_MAX / (tile_width * _overflow_scale_factor ))) { state->errcode = IMAGING_CODEC_BROKEN; state->state = J2K_STATE_FAILED; goto quick_exit; @@ -548,7 +569,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, state->errcode = IMAGING_CODEC_END; state->state = J2K_STATE_DONE; - ret = (int)ImagingIncrementalCodecBytesInBuffer(encoder); + ret = -1; quick_exit: if (codec) @@ -564,32 +585,17 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, 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 j2k_encode_entry(im, state); } - return ImagingIncrementalCodecPushBuffer(context->encoder, buf, bytes); + return -1; } /* -------------------------------------------------------------------- */ @@ -600,19 +606,16 @@ int ImagingJpeg2KEncodeCleanup(ImagingCodecState state) { JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context; - if (context->quality_layers && context->encoder) - Py_DECREF(context->quality_layers); + if (context->quality_layers) { + Py_XDECREF(context->quality_layers); + context->quality_layers = NULL; + } if (context->error_msg) free ((void *)context->error_msg); context->error_msg = NULL; - if (context->encoder) - ImagingIncrementalCodecDestroy(context->encoder); - - /* Prevent multiple calls to ImagingIncrementalCodecDestroy */ - context->encoder = NULL; return -1; } diff --git a/libImaging/codec_fd.c b/libImaging/codec_fd.c new file mode 100644 index 000000000..33954ece0 --- /dev/null +++ b/libImaging/codec_fd.c @@ -0,0 +1,79 @@ +#include "Python.h" +#include "Imaging.h" +#include "../py3.h" + + +Py_ssize_t +_imaging_read_pyFd(PyObject *fd, char* dest, Py_ssize_t bytes) +{ + /* dest should be a buffer bytes long, returns length of read + -1 on error */ + + PyObject *result; + char *buffer; + Py_ssize_t length; + int bytes_result; + + result = PyObject_CallMethod(fd, "read", "n", bytes); + + bytes_result = PyBytes_AsStringAndSize(result, &buffer, &length); + if (bytes_result == -1) { + goto err; + } + + if (length > bytes) { + goto err; + } + + memcpy(dest, buffer, length); + + Py_DECREF(result); + return length; + + err: + Py_DECREF(result); + return -1; + +} + +Py_ssize_t +_imaging_write_pyFd(PyObject *fd, char* src, Py_ssize_t bytes) +{ + + PyObject *result; + PyObject *byteObj; + + byteObj = PyBytes_FromStringAndSize(src, bytes); + result = PyObject_CallMethod(fd, "write", "O", byteObj); + + Py_DECREF(byteObj); + Py_DECREF(result); + + return bytes; + +} + +int +_imaging_seek_pyFd(PyObject *fd, Py_ssize_t offset, int whence) +{ + PyObject *result; + + result = PyObject_CallMethod(fd, "seek", "ni", offset, whence); + + Py_DECREF(result); + return 0; + +} + +Py_ssize_t +_imaging_tell_pyFd(PyObject *fd) +{ + PyObject *result; + Py_ssize_t location; + + result = PyObject_CallMethod(fd, "tell", NULL); + location = PyInt_AsSsize_t(result); + + Py_DECREF(result); + return location; +} diff --git a/py3.h b/py3.h index 8adfac081..a2a1e264f 100644 --- a/py3.h +++ b/py3.h @@ -19,6 +19,7 @@ #define PyInt_FromLong PyLong_FromLong #define PyInt_AS_LONG PyLong_AS_LONG #define PyInt_FromSsize_t PyLong_FromSsize_t +#define PyInt_AsSsize_t PyLong_AsSsize_t #else /* PY_VERSION_HEX < 0x03000000 */ #define PY_ARG_BYTES_LENGTH "s#" diff --git a/setup.py b/setup.py index cf420debc..bf9adb718 100644 --- a/setup.py +++ b/setup.py @@ -35,8 +35,8 @@ _LIB_IMAGING = ( "QuantHash", "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point", "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", - "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Incremental", - "Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur", "QuantPngQuant") + "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode", "Jpeg2KDecode", + "Jpeg2KEncode", "BoxBlur", "QuantPngQuant", "codec_fd") DEBUG = False